import «Study»

#eval 1+1

#eval String.append "Hello " "Dude"

#eval 41 + 19

#eval String.append "A" (String.append "B" "C")

#eval if 3 == 3 then 5 else 7

def main : IO Unit :=
  IO.println s!"Hello, {hello}!"

def fac: Nat -> Nat
  | 0 => 1
  | n + 1 => (n + 1) * fac n

#eval fac 6

def ack: Nat -> Nat -> Nat
  | 0, n => n + 1
  | m +1, 0=> ack m 1
  | m+1, n+1 => ack m (ack (m+1) n)
  termination_by ack m n => (m, n)

inductive State where
  | A | B | C | D | Halt
  deriving Repr

open State

def step : State -> State
  | A => B
  | B => Halt
  | C => D
  | D => C
  | Halt => Halt

def run : Nat -> State -> State
 | 0, s => s
 | n + 1, s => step (run n s)

#eval run 42 A

def A_halts_prop: Prop :=
  exists n: Nat, run n A = Halt

theorem A_halts : A_halts_prop := by
  exists 2

def C_halts_prop: Prop :=
  ∃ n: Nat, run n C = Halt

def C_hangs_prop :=
  ¬ ∃ n: Nat, run n C = Halt

def C_hangs_prop2 :=
  ∀ n: Nat, run n C ≠ Halt

theorem C_D_loop:
  ∀ n,
  run n C = C ∨ run n C = D
  := by
  intro n
  induction n with
  | zero => apply Or.inl; rw [run]
  | succ n ih =>
    cases ih with
    | inl hc => apply Or.inr; rw [run, step, hc]
    | inr hd => apply Or.inl; rw [run, step, hd]

theorem C_hangs: C_hangs_prop2 := by
  intro n
  have is_c_or_d := C_D_loop n
  cases is_c_or_d with
  | inl is_c => rw [is_c]; simp
  | inr is_d => rw [is_d]; simp


/-
Copyright (c) 2020 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon
-/
import data.bitvec.core
import data.fin
import tactic.norm_num
import tactic.monotonicity

namespace bitvec

instance (n : ℕ) : preorder (bitvec n) :=
preorder.lift bitvec.to_nat

/-- convert `fin` to `bitvec` -/
def of_fin {n : ℕ} (i : fin $ 2^n) : bitvec n :=
bitvec.of_nat _ i.val

lemma of_fin_val {n : ℕ} (i : fin $ 2^n) : (of_fin i).to_nat = i.val :=
by rw [of_fin,to_nat_of_nat,nat.mod_eq_of_lt]; apply i.is_lt

/-- convert `bitvec` to `fin` -/
def to_fin {n : ℕ} (i : bitvec n) : fin $ 2^n :=
@fin.of_nat' _ ⟨pow_pos (by norm_num) _⟩ i.to_nat

lemma add_lsb_eq_twice_add_one {x b} :
  add_lsb x b = 2 * x + cond b 1 0 :=
by simp [add_lsb,two_mul]

lemma to_nat_eq_foldr_reverse {n : ℕ} (v : bitvec n) :
  v.to_nat = v.to_list.reverse.foldr (flip add_lsb) 0 :=
by rw [list.foldr_reverse, flip]; refl

lemma to_nat_lt {n : ℕ} (v : bitvec n) : v.to_nat < 2^n :=
begin
  suffices : v.to_nat + 1 ≤ 2 ^ n, { simpa },
  rw to_nat_eq_foldr_reverse,
  cases v with xs h,
  dsimp [bitvec.to_nat,bits_to_nat],
  rw ← list.length_reverse at h,
  generalize_hyp : xs.reverse = ys at ⊢ h, clear xs,
  induction ys generalizing n,
  { simp [← h] },
  { simp only [←h, pow_add, flip, list.length, list.foldr, pow_one],
    rw [add_lsb_eq_twice_add_one],
    transitivity 2 * list.foldr (λ (x : bool) (y : ℕ), add_lsb y x) 0 ys_tl + 2 * 1,
    { ac_mono, rw two_mul, mono, cases ys_hd; simp },
    { rw ← left_distrib, ac_mono, norm_num,
      apply ys_ih, refl } },
end

lemma add_lsb_div_two {x b} : add_lsb x b / 2 = x :=
by cases b; simp only [nat.add_mul_div_left, add_lsb, ←two_mul, add_comm, nat.succ_pos',
                       nat.mul_div_right, gt_iff_lt, zero_add, cond]; norm_num

lemma to_bool_add_lsb_mod_two {x b} : to_bool (add_lsb x b % 2 = 1) = b :=
by cases b; simp only [to_bool_iff, nat.add_mul_mod_self_left, add_lsb, ←two_mul, add_comm,
                       bool.to_bool_false, nat.mul_mod_right, zero_add, cond, zero_ne_one]; norm_num

lemma of_nat_to_nat  {n : ℕ} (v : bitvec n) : bitvec.of_nat _ v.to_nat = v :=
begin
  cases v with xs h,
  ext1,
  change vector.to_list _ = xs,
  dsimp [bitvec.to_nat,bits_to_nat],
  rw ← list.length_reverse at h,
  rw [← list.reverse_reverse xs,list.foldl_reverse],
  generalize_hyp : xs.reverse = ys at ⊢ h, clear xs,
  induction ys generalizing n,
  { cases h, simp [bitvec.of_nat] },
  { simp only [←nat.succ_eq_add_one, list.length] at h, subst n,
    simp only [bitvec.of_nat, vector.to_list_cons, vector.to_list_nil, list.reverse_cons,
      vector.to_list_append, list.foldr],
    erw [add_lsb_div_two, to_bool_add_lsb_mod_two],
    congr, apply ys_ih, refl }
end

lemma to_fin_val {n : ℕ} (v : bitvec n) : (to_fin v : ℕ) = v.to_nat :=
by rw [to_fin, fin.coe_of_nat_eq_mod', nat.mod_eq_of_lt]; apply to_nat_lt

lemma to_fin_le_to_fin_of_le {n} {v₀ v₁ : bitvec n} (h : v₀ ≤ v₁) : v₀.to_fin ≤ v₁.to_fin :=
show (v₀.to_fin : ℕ) ≤ v₁.to_fin,
by rw [to_fin_val,to_fin_val]; exact h

lemma of_fin_le_of_fin_of_le {n : ℕ} {i j : fin (2^n)} (h : i ≤ j) : of_fin i ≤ of_fin j :=
show (bitvec.of_nat n i).to_nat ≤ (bitvec.of_nat n j).to_nat,
by { simp only [to_nat_of_nat, nat.mod_eq_of_lt, fin.is_lt], exact h }

lemma to_fin_of_fin {n} (i : fin $ 2^n) : (of_fin i).to_fin = i :=
fin.eq_of_veq (by simp [to_fin_val, of_fin, to_nat_of_nat, nat.mod_eq_of_lt, i.is_lt])

lemma of_fin_to_fin {n} (v : bitvec n) : of_fin (to_fin v) = v :=
by dsimp [of_fin]; rw [to_fin_val, of_nat_to_nat]

end bitvec


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

From: https://github.com/leanprover-community/mathlib/pull/10119
-/
import .homogeneous
import cicm2022.internal.graded_ring

/-! # Grading

This file contains an instance of `mv_polynomial R σ` as a `ℕ`-graded algebra.

## Tags

graded algebra, polynomial
-/

namespace mv_polynomial

open mv_polynomial direct_sum
open_locale direct_sum big_operators

variables (R : Type*) [comm_ring R] (σ : Type*)

/-
For a multivariable polynomial `p`, we can decompose `p` as a direct sum of monomials
-/
noncomputable def decompose (p : mv_polynomial σ R) :
  ⨁ i, (homogeneous_submodule σ R i) :=
∑ i in finset.range (p.total_degree + 1), of (λ i, (homogeneous_submodule σ R i)) i
  ⟨(homogeneous_component i p), homogeneous_component_is_homogeneous i _⟩

lemma homogeneous_component_of_direct_sum
  (i : ℕ) (x : ⨁ i, homogeneous_submodule σ R i) :
homogeneous_component i (direct_sum.coe_add_monoid_hom _ x) = x i :=
begin
  classical,
  rw [←sum_support_of _ x, add_monoid_hom.map_sum, linear_map.map_sum],
  simp_rw [direct_sum.coe_add_monoid_hom_of],
  simp only [add_subgroup.coe_subtype, submodule.coe_sum, dfinsupp.finset_sum_apply],
  apply finset.sum_congr rfl,
  intros j hj,
  rw [← subtype.val_eq_coe, homogeneous_component_homogeneous_polynomial i j _
    (by simp only [mem_homogeneous_submodule]; convert (x j).2)],
  split_ifs,
  { rw [h, of_eq_same], refl, },
  { rw of_eq_of_ne _ _ _ _ (ne.symm h), refl },
end

lemma left_inv (x) :
  (decompose R σ) (direct_sum.coe_add_monoid_hom (λ (i : ℕ), homogeneous_submodule σ R i) x) = x :=
begin
  classical,
  -- haveI :  Π (i : ℕ) (x : homogeneous_submodule σ R i), decidable (x ≠ 0) :=
  --   λ _, classical.dec_pred _,
  rw decompose,
  simp_rw [homogeneous_component_of_direct_sum],
  conv_rhs { rw ←direct_sum.sum_support_of _ x },
  rw [show finset.range ((direct_sum.coe_add_monoid_hom _ x).total_degree + 1) = x.support ∪
      ((finset.range ((direct_sum.coe_add_monoid_hom _ x).total_degree + 1)) \ x.support), begin
      rw finset.union_sdiff_of_subset (λ a ha, _),
      simp only [ne.def, dfinsupp.mem_support_to_fun, finset.mem_range] at ha ⊢,
      contrapose! ha,
      have := homogeneous_component_eq_zero _ _ ha,
      rw [homogeneous_component_of_direct_sum, ← subtype.val_eq_coe] at this,
      rw [subtype.ext_iff_val, this, subtype.val_eq_coe, submodule.coe_zero],
    end, finset.sum_union, show ∑ (j : ℕ) in finset.range (((direct_sum.coe_add_monoid_hom _) x).total_degree + 1) \
      dfinsupp.support x,
      (of (λ (i : ℕ), ↥(homogeneous_submodule σ R i)) j) ⟨(x j), _⟩ = 0, from _, add_zero,
    finset.sum_congr rfl (λ i hi, _)],
  { simp only [set_like.eta], },
  { rw ←finset.sum_const_zero,
    apply finset.sum_congr rfl (λ i hi, _),
    simp only [set_like.eta, not_not, finset.mem_sdiff, ne.def, dfinsupp.mem_support_to_fun,
      finset.mem_range] at hi ⊢,
    rw [hi.2, map_zero], },
  { exact disjoint_sdiff_self_right },
end

lemma right_inv (x) : direct_sum.coe_add_monoid_hom _ (decompose R σ x) = x :=
begin
  rw [decompose, add_monoid_hom.map_sum],
  conv_rhs { rw ← sum_homogeneous_component x },
  apply finset.sum_congr rfl (λ _ _, _),
  rw [direct_sum.coe_add_monoid_hom_of, ← subtype.val_eq_coe],
end

noncomputable instance graded_algebra :
  graded_algebra (λ i : ℕ, (homogeneous_submodule σ R i)) :=
{ one_mem := is_homogeneous_one σ R,
  mul_mem := λ i j x y hx hy, is_homogeneous.mul hx hy,
  decompose' := decompose R σ,
  left_inv := λ x, by apply right_inv,
  right_inv := λ x, by apply left_inv }

end mv_polynomial


import .love06_monads_demo

/-! # LoVe Exercise 6: Monads -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1: A State Monad with Failure

We introduce a richer notion of lawful monad that provides an `orelse`
operator `<|>` satisfying some laws, given below. `emp` denotes failure.
`x <|> y` tries `x` first, falling back on `y` on failure. -/

@[class] structure lawful_monad_with_orelse (m : Type → Type)
  extends lawful_monad m, has_orelse m : Type 1 :=
(emp {} {α : Type} : m α)
(emp_orelse {α : Type} (a : m α) :
  (emp <|> a) = a)
(orelse_emp {α : Type} (a : m α) :
  (a <|> emp) = a)
(orelse_assoc {α : Type} (a b c : m α) :
  ((a <|> b) <|> c) = (a <|> (b <|> c)))
(emp_bind {α β : Type} (f : α → m β) :
  (emp >>= f) = emp)
(bind_emp {α β : Type} (f : m α) :
  (f >>= (λa, emp : α → m β)) = emp)

/-! 1.1. We set up the `option` type constructor to be a
`lawful_monad_with_orelse`. Complete the proofs.

Hint: Use `simp [(>>=)]` if you want to unfold the definition of the bind
operator. -/

def option.orelse {α : Type} : option α → option α → option α
| option.none     ma' := ma'
| (option.some a) _   := option.some a

@[instance] def lawful_monad_with_orelse_option :
  lawful_monad_with_orelse option :=
{ emp          := λα, option.none,
  orelse       := @option.orelse,
  emp_orelse   :=
    sorry,
  orelse_emp   :=
    begin
      intros α a,
      cases' a,
      { refl },
      { refl }
    end,
  orelse_assoc :=
    sorry,
  emp_bind     :=
    begin
      intros α β f,
      refl
    end,
  bind_emp     :=
    sorry,
  .. option.lawful_monad }

@[simp] lemma option.some_bind {α β : Type} (a : α) (g : α → option β) :
  (option.some a >>= g) = g a :=
sorry

/-! Let us enable some convenient pattern matching syntax, by instantiating
Lean's `monad_fail` type class. (Do not worry if you do not understand what
we are referring to.) -/

@[instance] def lawful_monad_with_orelse.monad_fail {m : Type → Type}
  [lawful_monad_with_orelse m] : monad_fail m :=
{ fail := λα msg, lawful_monad_with_orelse.emp }

/-! Now we can write definitions such as the following: -/

def first_of_three {m : Type → Type} [lawful_monad_with_orelse m]
  (c : m (list ℕ)) : m ℕ :=
do
  [n, _, _] ← c,
  pure n

#eval first_of_three (option.some [1])
#eval first_of_three (option.some [1, 2, 3])
#eval first_of_three (option.some [1, 2, 3, 4])

/-! Using `lawful_monad_with_orelse` and the `monad_fail` syntax, we can give a
concise definition for the `sum_2_5_7` function seen in the lecture. -/

def sum_2_5_7₇ {m : Type → Type} [lawful_monad_with_orelse m]
  (c : m (list ℕ)) : m ℕ :=
do
  (_ :: n2 :: _ :: _ :: n5 :: _ :: n7 :: _) ← c,
  pure (n2 + n5 + n7)

/-! 1.2. Now we are ready to define `faction σ` ("eff action"): a monad with an
internal state of type `σ` that can fail (unlike `action σ`).

We start with defining `faction σ α`, where `σ` is the type of the internal
state, and `α` is the type of the value stored in the monad. We use `option` to
model failure. This means we can also use the monad operations of `option` when
defining the monad operations on `faction`.

Hints:

* Remember that `faction σ α` is an alias for a function type, so you can use
  pattern matching and `λs, …` to define values of type `faction σ α`.

* `faction` is very similar to `action` from the lecture's demo. You can look
  there for inspiration. -/

def faction (σ : Type) (α : Type) : Type :=
σ → option (α × σ)

/-! 1.3. Define the `get` and `set` function for `faction`, where `get` returns
the state passed along the state monad and `set s` changes the state to `s`. -/

def get {σ : Type} : faction σ σ :=
sorry

def set {σ : Type} (s : σ) : faction σ unit :=
sorry

/-! We set up the `>>=` syntax on `faction`: -/

def faction.bind {σ α β : Type} (f : faction σ α) (g : α → faction σ β) :
  faction σ β
| s := f s >>= (λas, g (prod.fst as) (prod.snd as))

@[instance] def faction.has_bind {σ : Type} : has_bind (faction σ) :=
{ bind := @faction.bind σ }

lemma faction.bind_apply {σ α β : Type} (f : faction σ α) (g : α → faction σ β)
    (s : σ) :
  (f >>= g) s = (f s >>= (λas, g (prod.fst as) (prod.snd as))) :=
by refl

/-! 1.4. Define the operator `pure` for `faction`, in such a way that it will
satisfy the three laws. -/

def faction.pure {σ α : Type} (a : α) : faction σ α :=
sorry

/-! We set up the syntax for `pure` on `faction`: -/

@[instance] def faction.has_pure {σ : Type} : has_pure (faction σ) :=
{ pure := @faction.pure σ }

lemma faction.pure_apply {σ α : Type} (a : α) (s : σ) :
  (pure a : faction σ α) s = option.some (a, s) :=
by refl

/-! 1.3. Register `faction` as a monad.

Hints:

* The `funext` lemma is useful when you need to prove equality between two
  functions.

* `cases' f s` only works when `f s` appears in your goal, so you may need to
  unfold some constants before you can invoke `cases'`. -/

@[instance] def faction.lawful_monad {σ : Type} : lawful_monad (faction σ) :=
{ pure_bind  :=
    begin
      intros α β a f,
      apply funext,
      intro s,
      refl
    end,
  bind_pure  :=
    sorry,
  bind_assoc :=
    sorry,
  .. faction.has_bind,
  .. faction.has_pure }

/-! ## Question 2: Kleisli Operator

The Kleisli operator `>=>` (not to be confused with `>>=`) is useful for
pipelining effectful functions. Note that `λa, f a >>= g` is to be parsed as
`λa, (f a >>= g)`, not as `(λa, f a) >>= g`. -/

def kleisli {m : Type → Type} [lawful_monad m] {α β γ : Type} (f : α → m β)
  (g : β → m γ) : α → m γ :=
λa, f a >>= g

infixr ` >=> ` : 90 := kleisli

/-! 2.1. Prove that `pure` is a left and right unit for the Kleisli operator. -/

lemma pure_kleisli {m : Type → Type} [lawful_monad m] {α β : Type}
    (f : α → m β) :
  (pure >=> f) = f :=
sorry

lemma kleisli_pure {m : Type → Type} [lawful_monad m] {α β : Type}
    (f : α → m β) :
  (f >=> pure) = f :=
sorry

/-! 2.2. Prove that the Kleisli operator is associative. -/

lemma kleisli_assoc {m : Type → Type} [lawful_monad m] {α β γ δ : Type}
    (f : α → m β) (g : β → m γ) (h : γ → m δ) :
  ((f >=> g) >=> h) = (f >=> (g >=> h)) :=
sorry

end LoVe


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

! This file was ported from Lean 3 source module topology.instances.complex
! leanprover-community/mathlib commit 78ac1db34d90519b8e8f7bfe39cb264a6a117db2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Algebra.UniformField
import Mathbin.Analysis.Complex.Basic
import Mathbin.FieldTheory.Adjoin

/-!
# Some results about the topology of ℂ
-/

section ComplexSubfield

open Complex Set

open ComplexConjugate

/-- The only closed subfields of `ℂ` are `ℝ` and `ℂ`. -/
theorem Complex.subfield_eq_of_closed {K : Subfield ℂ} (hc : IsClosed (K : Set ℂ)) :
    K = ofReal.fieldRange ∨ K = ⊤ :=
  by
  suffices range (coe : ℝ → ℂ) ⊆ K
    by
    rw [range_subset_iff, ← coe_algebra_map] at this
    have :=
      (Subalgebra.isSimpleOrder_of_finrank finrank_real_complex).eq_bot_or_eq_top
        (Subfield.toIntermediateField K this).toSubalgebra
    simp_rw [← SetLike.coe_set_eq] at this⊢
    convert this using 2
    simpa only [RingHom.coe_fieldRange, Algebra.coe_bot, coe_algebra_map]
  suffices range (coe : ℝ → ℂ) ⊆ closure (Set.range ((coe : ℝ → ℂ) ∘ (coe : ℚ → ℝ)))
    by
    refine' subset_trans this _
    rw [← IsClosed.closure_eq hc]
    apply closure_mono
    rintro _ ⟨_, rfl⟩
    simp only [Function.comp_apply, of_real_rat_cast, SetLike.mem_coe, SubfieldClass.coe_rat_mem]
  nth_rw 2 [range_comp]
  refine' subset_trans _ (image_closure_subset_closure_image continuous_of_real)
  rw [DenseRange.closure_range rat.dense_embedding_coe_real.dense]
  simp only [image_univ]
#align complex.subfield_eq_of_closed Complex.subfield_eq_of_closed

/- ./././Mathport/Syntax/Translate/Tactic/Mathlib/Misc2.lean:301:22: continuitity! not supported at the moment -/
/- ./././Mathport/Syntax/Translate/Tactic/Mathlib/Misc2.lean:301:22: continuitity! not supported at the moment -/
/-- Let `K` a subfield of `ℂ` and let `ψ : K →+* ℂ` a ring homomorphism. Assume that `ψ` is uniform
continuous, then `ψ` is either the inclusion map or the composition of the inclusion map with the
complex conjugation. -/
theorem Complex.uniformContinuous_ringHom_eq_id_or_conj (K : Subfield ℂ) {ψ : K →+* ℂ}
    (hc : UniformContinuous ψ) : ψ.toFun = K.Subtype ∨ ψ.toFun = conj ∘ K.Subtype :=
  by
  letI : TopologicalDivisionRing ℂ := TopologicalDivisionRing.mk
  letI : TopologicalRing K.topological_closure :=
    Subring.topologicalRing K.topological_closure.to_subring
  set ι : K → K.topological_closure := Subfield.inclusion K.le_topological_closure
  have ui : UniformInducing ι :=
    ⟨by
      erw [uniformity_subtype, uniformity_subtype, Filter.comap_comap]
      congr ⟩
  let di := ui.dense_inducing _
  · -- extψ : closure(K) →+* ℂ is the extension of ψ : K →+* ℂ
    let extψ := DenseInducing.extendRingHom ui di.dense hc
    haveI := (uniformContinuous_uniformly_extend ui di.dense hc).Continuous
    cases Complex.subfield_eq_of_closed (Subfield.isClosed_topologicalClosure K)
    · left
      let j := RingEquiv.subfieldCongr h
      -- ψ₁ is the continuous ring hom `ℝ →+* ℂ` constructed from `j : closure (K) ≃+* ℝ`
      -- and `extψ : closure (K) →+* ℂ`
      let ψ₁ := RingHom.comp extψ (RingHom.comp j.symm.to_ring_hom of_real.range_restrict)
      ext1 x
      rsuffices ⟨r, hr⟩ : ∃ r : ℝ, of_real.range_restrict r = j (ι x)
      · have :=
          RingHom.congr_fun (ring_hom_eq_of_real_of_continuous (by continuity : Continuous ψ₁)) r
        rw [RingHom.comp_apply, RingHom.comp_apply, hr, RingEquiv.toRingHom_eq_coe] at this
        convert this using 1
        · exact (DenseInducing.extend_eq di hc.continuous _).symm
        · rw [← of_real.coe_range_restrict, hr]
          rfl
      obtain ⟨r, hr⟩ := SetLike.coe_mem (j (ι x))
      exact ⟨r, Subtype.ext hr⟩
    · -- ψ₁ is the continuous ring hom `ℂ →+* ℂ` constructed from `closure (K) ≃+* ℂ`
      -- and `extψ : closure (K) →+* ℂ`
      let ψ₁ :=
        RingHom.comp extψ
          (RingHom.comp (RingEquiv.subfieldCongr h).symm.toRingHom
            (@Subfield.topEquiv ℂ _).symm.toRingHom)
      cases' ring_hom_eq_id_or_conj_of_continuous (by continuity : Continuous ψ₁) with h h
      · left
        ext1 z
        convert RingHom.congr_fun h z using 1
        exact (DenseInducing.extend_eq di hc.continuous z).symm
      · right
        ext1 z
        convert RingHom.congr_fun h z using 1
        exact (DenseInducing.extend_eq di hc.continuous z).symm
  · let j : { x // x ∈ closure (id '' { x | (K : Set ℂ) x }) } → (K.topological_closure : Set ℂ) :=
      fun x =>
      ⟨x, by
        convert x.prop
        simpa only [id.def, Set.image_id'] ⟩
    convert DenseRange.comp (Function.Surjective.denseRange _)
        (DenseEmbedding.subtype denseEmbedding_id (K : Set ℂ)).dense (by continuity : Continuous j)
    rintro ⟨y, hy⟩
    use
      ⟨y, by
        convert hy
        simpa only [id.def, Set.image_id'] ⟩
    simp only [Subtype.mk_eq_mk, Subtype.coe_mk]
#align complex.uniform_continuous_ring_hom_eq_id_or_conj Complex.uniformContinuous_ringHom_eq_id_or_conj

end ComplexSubfield


/-
Copyright (c) 2021 Andrew Yang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andrew Yang
-/
import topology.separation
import topology.continuous_function.basic

/-!
# Sober spaces

A quasi-sober space is a topological space where every
irreducible closed subset has a generic point.
A sober space is a quasi-sober space where every irreducible closed subset
has a *unique* generic point. This is if and only if the space is T0, and thus sober spaces can be
stated via `[quasi_sober α] [t0_space α]`.

## Main definition

* `specialization_order` : specialization gives a partial order on a T0 space.
* `is_generic_point` : `x` is the generic point of `S` if `S` is the closure of `x`.
* `quasi_sober` : A space is quasi-sober if every irreducible closed subset has a generic point.

-/

variables {α β : Type*} [topological_space α] [topological_space β]

section generic_point

/-- `x` is a generic point of `S` if `S` is the closure of `x`. -/
def is_generic_point (x : α) (S : set α) : Prop := closure ({x} : set α) = S

lemma is_generic_point_def {x : α} {S : set α} : is_generic_point x S ↔ closure ({x} : set α) = S :=
iff.rfl

lemma is_generic_point.def {x : α} {S : set α} (h : is_generic_point x S) :
  closure ({x} : set α) = S := h

lemma is_generic_point_closure {x : α} : is_generic_point x (closure ({x} : set α)) := refl _

variables {x : α} {S : set α} (h : is_generic_point x S)

include h

lemma is_generic_point.specializes {y : α} (h' : y ∈ S) :
  x ⤳ y :=
specializes_iff_mem_closure.2 $ h.def.symm ▸ h'

lemma is_generic_point.mem : x ∈ S :=
h.def ▸ subset_closure (set.mem_singleton x)

lemma is_generic_point.is_closed : is_closed S :=
h.def ▸ is_closed_closure

lemma is_generic_point.is_irreducible : is_irreducible S :=
h.def ▸ is_irreducible_singleton.closure

lemma is_generic_point.eq [t0_space α] {y : α} (h' : is_generic_point y S) : x = y :=
((h.specializes h'.mem).antisymm (h'.specializes h.mem)).eq

lemma is_generic_point.mem_open_set_iff
  {U : set α} (hU : is_open U) : x ∈ U ↔ (S ∩ U).nonempty :=
⟨λ h', ⟨x, h.mem, h'⟩,
    λ h', specializes_iff_forall_open.mp (h.specializes h'.some_spec.1) U hU h'.some_spec.2⟩

lemma is_generic_point.disjoint_iff
  {U : set α} (hU : is_open U) : disjoint S U ↔ x ∉ U :=
by rw [h.mem_open_set_iff hU, ← set.ne_empty_iff_nonempty, not_not, set.disjoint_iff_inter_eq_empty]

lemma is_generic_point.mem_closed_set_iff
  {Z : set α} (hZ : is_closed Z) : x ∈ Z ↔ S ⊆ Z :=
by rw [← is_generic_point_def.mp h, hZ.closure_subset_iff, set.singleton_subset_iff]

lemma is_generic_point.image {f : α → β} (hf : continuous f) :
  is_generic_point (f x) (closure (f '' S)) :=
begin
  rw [is_generic_point_def, ← is_generic_point_def.mp h],
  apply le_antisymm,
  { exact closure_mono
      (set.singleton_subset_iff.mpr ⟨_, subset_closure $ set.mem_singleton x, rfl⟩) },
  { convert is_closed_closure.closure_subset_iff.mpr (image_closure_subset_closure_image hf),
    rw set.image_singleton }
end

omit h

lemma is_generic_point_iff_forall_closed {x : α} {S : set α} (hS : is_closed S) (hxS : x ∈ S) :
  is_generic_point x S ↔ ∀ (Z : set α) (hZ : is_closed Z) (hxZ : x ∈ Z), S ⊆ Z :=
begin
  split,
  { intros h Z hZ hxZ, exact (h.mem_closed_set_iff hZ).mp hxZ },
  { intro h,
    apply le_antisymm,
    { rwa [set.le_eq_subset, hS.closure_subset_iff, set.singleton_subset_iff] },
    { exact h _ is_closed_closure (subset_closure $ set.mem_singleton x) } }
end

end generic_point

section sober

/-- A space is sober if every irreducible closed subset has a generic point. -/
@[mk_iff]
class quasi_sober (α : Type*) [topological_space α] : Prop :=
(sober : ∀ {S : set α} (hS₁ : is_irreducible S) (hS₂ : is_closed S), ∃ x, is_generic_point x S)

/-- A generic point of the closure of an irreducible space. -/
noncomputable
def is_irreducible.generic_point [quasi_sober α] {S : set α} (hS : is_irreducible S) : α :=
(quasi_sober.sober hS.closure is_closed_closure).some

lemma is_irreducible.generic_point_spec [quasi_sober α] {S : set α} (hS : is_irreducible S) :
  is_generic_point hS.generic_point (closure S) :=
(quasi_sober.sober hS.closure is_closed_closure).some_spec

@[simp] lemma is_irreducible.generic_point_closure_eq [quasi_sober α] {S : set α}
  (hS : is_irreducible S) : closure ({hS.generic_point} : set α) = closure S :=
hS.generic_point_spec

variable (α)

/-- A generic point of a sober irreducible space. -/
noncomputable
def generic_point [quasi_sober α] [irreducible_space α] : α :=
(irreducible_space.is_irreducible_univ α).generic_point

lemma generic_point_spec [quasi_sober α] [irreducible_space α] :
  is_generic_point (generic_point α) ⊤ :=
by simpa using (irreducible_space.is_irreducible_univ α).generic_point_spec

@[simp]
lemma generic_point_closure [quasi_sober α] [irreducible_space α] :
  closure ({generic_point α} : set α) = ⊤ := generic_point_spec α

variable {α}

lemma generic_point_specializes [quasi_sober α] [irreducible_space α] (x : α) :
  generic_point α ⤳ x := (is_irreducible.generic_point_spec _).specializes (by simp)

local attribute [instance, priority 10] specialization_order

/-- The closed irreducible subsets of a sober space bijects with the points of the space. -/
noncomputable
def irreducible_set_equiv_points [quasi_sober α] [t0_space α] :
  { s : set α | is_irreducible s ∧ is_closed s } ≃o α :=
{ to_fun := λ s, s.prop.1.generic_point,
  inv_fun := λ x, ⟨closure ({x} : set α), is_irreducible_singleton.closure, is_closed_closure⟩,
  left_inv := λ s,
    subtype.eq $ eq.trans (s.prop.1.generic_point_spec) $ closure_eq_iff_is_closed.mpr s.2.2,
  right_inv := λ x, is_irreducible_singleton.closure.generic_point_spec.eq
      (by { convert is_generic_point_closure using 1, rw closure_closure }),
  map_rel_iff' := λ s t, by { change _ ⤳ _ ↔ _, rw specializes_iff_closure_subset,
    simp [s.prop.2.closure_eq, t.prop.2.closure_eq, ← subtype.coe_le_coe] } }

lemma closed_embedding.quasi_sober {f : α → β} (hf : closed_embedding f) [quasi_sober β] :
  quasi_sober α :=
begin
  constructor,
  intros S hS hS',
  have hS'' := hS.image f hf.continuous.continuous_on,
  obtain ⟨x, hx⟩ := quasi_sober.sober hS'' (hf.is_closed_map _ hS'),
  obtain ⟨y, hy, rfl⟩ := hx.mem,
  use y,
  change _ = _ at hx,
  apply set.image_injective.mpr hf.inj,
  rw [← hx, ← hf.closure_image_eq, set.image_singleton]
end

lemma open_embedding.quasi_sober {f : α → β} (hf : open_embedding f) [quasi_sober β] :
  quasi_sober α :=
begin
  constructor,
  intros S hS hS',
  have hS'' := hS.image f hf.continuous.continuous_on,
  obtain ⟨x, hx⟩ := quasi_sober.sober hS''.closure is_closed_closure,
  obtain ⟨T, hT, rfl⟩ := hf.to_inducing.is_closed_iff.mp hS',
  rw set.image_preimage_eq_inter_range at hx hS'',
  have hxT : x ∈ T,
  { rw ← hT.closure_eq, exact closure_mono (set.inter_subset_left _ _) hx.mem },
  have hxU : x ∈ set.range f,
  { rw hx.mem_open_set_iff hf.open_range,
    refine set.nonempty.mono _ hS''.1,
    simpa using subset_closure },
  rcases hxU with ⟨y, rfl⟩,
  use y,
  change _ = _,
  rw [hf.to_embedding.closure_eq_preimage_closure_image,
    set.image_singleton, (show _ = _, from hx)],
  apply set.image_injective.mpr hf.inj,
  ext z,
  simp only [set.image_preimage_eq_inter_range, set.mem_inter_eq, and.congr_left_iff],
  exact λ hy, ⟨λ h, hT.closure_eq ▸ closure_mono (set.inter_subset_left _ _) h,
    λ h, subset_closure ⟨h, hy⟩⟩
end

/-- A space is quasi sober if it can be covered by open quasi sober subsets. -/
lemma quasi_sober_of_open_cover (S : set (set α)) (hS : ∀ s : S, is_open (s : set α))
  [hS' : ∀ s : S, quasi_sober s] (hS'' : ⋃₀ S = ⊤) : quasi_sober α :=
begin
  rw quasi_sober_iff,
  intros t h h',
  obtain ⟨x, hx⟩ := h.1,
  obtain ⟨U, hU, hU'⟩ : x ∈ ⋃₀S := by { rw hS'', trivial },
  haveI : quasi_sober U := hS' ⟨U, hU⟩,
  have H : is_preirreducible (coe ⁻¹' t : set U) :=
    h.2.preimage (hS ⟨U, hU⟩).open_embedding_subtype_coe,
  replace H : is_irreducible (coe ⁻¹' t : set U) := ⟨⟨⟨x, hU'⟩, by simpa using hx⟩, H⟩,
  use H.generic_point,
  have := continuous_subtype_coe.closure_preimage_subset _ H.generic_point_spec.mem,
  rw h'.closure_eq at this,
  apply le_antisymm,
  { apply h'.closure_subset_iff.mpr, simpa using this },
  rw [← set.image_singleton, ← closure_closure],
  have := closure_mono (image_closure_subset_closure_image (@continuous_subtype_coe α _ U)),
  refine set.subset.trans _ this,
  rw H.generic_point_spec.def,
  refine (subset_closure_inter_of_is_preirreducible_of_is_open
    h.2 (hS ⟨U, hU⟩) ⟨x, hx, hU'⟩).trans (closure_mono _),
  rw ← subtype.image_preimage_coe,
  exact set.image_subset _ subset_closure,
end

@[priority 100]
instance t2_space.quasi_sober [t2_space α] : quasi_sober α :=
begin
  constructor,
  rintro S h -,
  obtain ⟨x, rfl⟩ := (is_irreducible_iff_singleton S).mp h,
  exact ⟨x, closure_singleton⟩
end

end sober


/-
Copyright (c) 2014 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module algebra.char_zero.defs
! leanprover-community/mathlib commit d6aae1bcbd04b8de2022b9b83a5b5b10e10c777d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Int.Cast.Defs
import Mathlib.Tactic.NormCast.Tactic

/-!

# Characteristic zero

A ring `R` is called of characteristic zero if every natural number `n` is non-zero when considered
as an element of `R`. Since this definition doesn't mention the multiplicative structure of `R`
except for the existence of `1` in this file characteristic zero is defined for additive monoids
with `1`.

## Main definition

`CharZero` is the typeclass of an additive monoid with one such that the natural homomorphism
from the natural numbers into it is injective.

## TODO

* Unify with `CharP` (possibly using an out-parameter)
-/

/-- Typeclass for monoids with characteristic zero.
  (This is usually stated on fields but it makes sense for any additive monoid with 1.)
*Warning*: for a semiring `R`, `CharZero R` and `CharP R 0` need not coincide.
* `CharZero R` requires an injection `ℕ ↪ R`;
* `CharP R 0` asks that only `0 : ℕ` maps to `0 : R` under the map `ℕ → R`.
For instance, endowing `{0, 1}` with addition given by `max` (i.e. `1` is absorbing), shows that
`CharZero {0, 1}` does not hold and yet `CharP {0, 1} 0` does.
This example is formalized in `counterexamples/char_p_zero_ne_char_zero`.
 -/
class CharZero (R) [AddMonoidWithOne R] : Prop where
  /-- An additive monoid with one has characteristic zero if the canonical map `ℕ → R` is
  injective. -/
  cast_injective : Function.Injective (Nat.cast : ℕ → R)
#align char_zero CharZero

theorem charZero_of_inj_zero [AddGroupWithOne R] (H : ∀ n : ℕ, (n : R) = 0 → n = 0) :
    CharZero R :=
  ⟨@fun m n h => by
    induction' m with m ih generalizing n
    · rw [H n]
      rw [← h, Nat.cast_zero]

    cases' n with n
    · apply H
      rw [h, Nat.cast_zero]

    simp only [Nat.cast_succ, add_right_cancel_iff] at h
    rwa [ih]⟩
#align char_zero_of_inj_zero charZero_of_inj_zero

namespace Nat

variable [AddMonoidWithOne R] [CharZero R]

theorem cast_injective : Function.Injective (Nat.cast : ℕ → R) :=
  CharZero.cast_injective
#align nat.cast_injective Nat.cast_injective

@[simp, norm_cast]
theorem cast_inj {m n : ℕ} : (m : R) = n ↔ m = n :=
  cast_injective.eq_iff
#align nat.cast_inj Nat.cast_inj

@[simp, norm_cast]
theorem cast_eq_zero {n : ℕ} : (n : R) = 0 ↔ n = 0 := by rw [← cast_zero, cast_inj]
#align nat.cast_eq_zero Nat.cast_eq_zero

@[norm_cast]
theorem cast_ne_zero {n : ℕ} : (n : R) ≠ 0 ↔ n ≠ 0 :=
  not_congr cast_eq_zero
#align nat.cast_ne_zero Nat.cast_ne_zero

theorem cast_add_one_ne_zero (n : ℕ) : (n + 1 : R) ≠ 0 := by
  -- porting note: old proof was `exact_mod_cast n.succ_ne_zero`
  norm_cast
  exact n.succ_ne_zero
#align nat.cast_add_one_ne_zero Nat.cast_add_one_ne_zero

@[simp, norm_cast]
theorem cast_eq_one {n : ℕ} : (n : R) = 1 ↔ n = 1 := by rw [← cast_one, cast_inj]
#align nat.cast_eq_one Nat.cast_eq_one

@[norm_cast]
theorem cast_ne_one {n : ℕ} : (n : R) ≠ 1 ↔ n ≠ 1 :=
  cast_eq_one.not
#align nat.cast_ne_one Nat.cast_ne_one

end Nat

namespace OfNat

variable [AddMonoidWithOne R] [CharZero R]

@[simp] lemma ofNat_ne_zero (n : ℕ) [h : n.AtLeastTwo] : (ofNat n : R) ≠ 0 :=
  Nat.cast_ne_zero.2 <| ne_of_gt <| lt_trans Nat.one_pos h.prop

@[simp] lemma zero_ne_ofNat (n : ℕ) [n.AtLeastTwo] : 0 ≠ (ofNat n : R) :=
  (ofNat_ne_zero n).symm

@[simp] lemma ofNat_ne_one (n : ℕ) [h : n.AtLeastTwo] : (ofNat n : R) ≠ 1 := by
  rw [← Nat.cast_eq_ofNat, ← @Nat.cast_one R, Ne.def, Nat.cast_inj]
  exact ne_of_gt h.prop

@[simp] lemma one_ne_ofNat (n : ℕ) [n.AtLeastTwo] : (1 : R) ≠ ofNat n :=
  (ofNat_ne_one n).symm

@[simp] lemma ofNat_eq_ofNat {m n : ℕ} [m.AtLeastTwo] [n.AtLeastTwo] :
    (ofNat m : R) = ofNat n ↔ (ofNat m : ℕ) = ofNat n :=
  Nat.cast_inj

end OfNat

namespace NeZero

instance charZero {M} {n : ℕ} [NeZero n] [AddMonoidWithOne M] [CharZero M] : NeZero (n : M) :=
  ⟨Nat.cast_ne_zero.mpr out⟩
#align ne_zero.char_zero NeZero.charZero

instance charZero_ofNat {M} {n : ℕ} [n.AtLeastTwo] [AddMonoidWithOne M] [CharZero M] :
    NeZero (OfNat.ofNat n : M) :=
  ⟨OfNat.ofNat_ne_zero n⟩

end NeZero


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.fintype.basic
import Mathlib.PostPort

universes u u_1 v u_2 x u_3

namespace Mathlib

/-!
# Finite sets

This file defines predicates `finite : set α → Prop` and `infinite : set α → Prop` and proves some
basic facts about finite sets.
-/

namespace set

/-- A set is finite if the subtype is a fintype, i.e. there is a
  list that enumerates its members. -/
def finite {α : Type u} (s : set α) :=
  Nonempty (fintype ↥s)

/-- A set is infinite if it is not finite. -/
def infinite {α : Type u} (s : set α) :=
  ¬finite s

/-- The subtype corresponding to a finite set is a finite type. Note
that because `finite` isn't a typeclass, this will not fire if it
is made into an instance -/
def finite.fintype {α : Type u} {s : set α} (h : finite s) : fintype ↥s :=
  Classical.choice h

/-- Get a finset from a finite set -/
def finite.to_finset {α : Type u} {s : set α} (h : finite s) : finset α :=
  to_finset s

@[simp] theorem finite.mem_to_finset {α : Type u} {s : set α} {h : finite s} {a : α} : a ∈ finite.to_finset h ↔ a ∈ s :=
  mem_to_finset

@[simp] theorem finite.to_finset.nonempty {α : Type u} {s : set α} (h : finite s) : finset.nonempty (finite.to_finset h) ↔ set.nonempty s :=
  (fun (this : (∃ (x : α), x ∈ finite.to_finset h) ↔ ∃ (x : α), x ∈ s) => this)
    (exists_congr fun (_x : α) => finite.mem_to_finset)

@[simp] theorem finite.coe_to_finset {α : Type u_1} {s : set α} (h : finite s) : ↑(finite.to_finset h) = s :=
  coe_to_finset s

theorem finite.exists_finset {α : Type u} {s : set α} : finite s → ∃ (s' : finset α), ∀ (a : α), a ∈ s' ↔ a ∈ s := sorry

theorem finite.exists_finset_coe {α : Type u} {s : set α} (hs : finite s) : ∃ (s' : finset α), ↑s' = s :=
  Exists.intro (finite.to_finset hs) (finite.coe_to_finset hs)

/-- Finite sets can be lifted to finsets. -/
protected instance finset.can_lift {α : Type u} : can_lift (set α) (finset α) :=
  can_lift.mk coe finite sorry

theorem finite_mem_finset {α : Type u} (s : finset α) : finite (set_of fun (a : α) => a ∈ s) :=
  Nonempty.intro (fintype.of_finset s fun (_x : α) => iff.rfl)

theorem finite.of_fintype {α : Type u} [fintype α] (s : set α) : finite s :=
  Nonempty.intro (set_fintype s)

theorem exists_finite_iff_finset {α : Type u} {p : set α → Prop} : (∃ (s : set α), finite s ∧ p s) ↔ ∃ (s : finset α), p ↑s := sorry

/-- Membership of a subset of a finite type is decidable.

Using this as an instance leads to potential loops with `subtype.fintype` under certain decidability
assumptions, so it should only be declared a local instance. -/
def decidable_mem_of_fintype {α : Type u} [DecidableEq α] (s : set α) [fintype ↥s] (a : α) : Decidable (a ∈ s) :=
  decidable_of_iff (a ∈ to_finset s) mem_to_finset

protected instance fintype_empty {α : Type u} : fintype ↥∅ :=
  fintype.of_finset ∅ sorry

theorem empty_card {α : Type u} : fintype.card ↥∅ = 0 :=
  rfl

@[simp] theorem empty_card' {α : Type u} {h : fintype ↥∅} : fintype.card ↥∅ = 0 := sorry

@[simp] theorem finite_empty {α : Type u} : finite ∅ :=
  Nonempty.intro set.fintype_empty

protected instance finite.inhabited {α : Type u} : Inhabited (Subtype fun (s : set α) => finite s) :=
  { default := { val := ∅, property := finite_empty } }

/-- A `fintype` structure on `insert a s`. -/
def fintype_insert' {α : Type u} {a : α} (s : set α) [fintype ↥s] (h : ¬a ∈ s) : fintype ↥(insert a s) :=
  fintype.of_finset (finset.mk (a ::ₘ finset.val (to_finset s)) sorry) sorry

theorem card_fintype_insert' {α : Type u} {a : α} (s : set α) [fintype ↥s] (h : ¬a ∈ s) : fintype.card ↥(insert a s) = fintype.card ↥s + 1 := sorry

@[simp] theorem card_insert {α : Type u} {a : α} (s : set α) [fintype ↥s] (h : ¬a ∈ s) {d : fintype ↥(insert a s)} : fintype.card ↥(insert a s) = fintype.card ↥s + 1 := sorry

theorem card_image_of_inj_on {α : Type u} {β : Type v} {s : set α} [fintype ↥s] {f : α → β} [fintype ↥(f '' s)] (H : ∀ (x : α), x ∈ s → ∀ (y : α), y ∈ s → f x = f y → x = y) : fintype.card ↥(f '' s) = fintype.card ↥s := sorry

theorem card_image_of_injective {α : Type u} {β : Type v} (s : set α) [fintype ↥s] {f : α → β} [fintype ↥(f '' s)] (H : function.injective f) : fintype.card ↥(f '' s) = fintype.card ↥s :=
  card_image_of_inj_on fun (_x : α) (_x_1 : _x ∈ s) (_x_2 : α) (_x_3 : _x_2 ∈ s) (h : f _x = f _x_2) => H h

protected instance fintype_insert {α : Type u} [DecidableEq α] (a : α) (s : set α) [fintype ↥s] : fintype ↥(insert a s) :=
  dite (a ∈ s) (fun (h : a ∈ s) => eq.mpr sorry (eq.mpr sorry _inst_2)) fun (h : ¬a ∈ s) => fintype_insert' s h

@[simp] theorem finite.insert {α : Type u} (a : α) {s : set α} : finite s → finite (insert a s) :=
  fun (ᾰ : finite s) =>
    nonempty.dcases_on ᾰ
      fun (ᾰ : fintype ↥s) => idRhs (Nonempty (fintype ↥(insert a s))) (Nonempty.intro (set.fintype_insert a s))

theorem to_finset_insert {α : Type u} [DecidableEq α] {a : α} {s : set α} (hs : finite s) : finite.to_finset (finite.insert a hs) = insert a (finite.to_finset hs) := sorry

theorem finite.induction_on {α : Type u} {C : set α → Prop} {s : set α} (h : finite s) (H0 : C ∅) (H1 : ∀ {a : α} {s : set α}, ¬a ∈ s → finite s → C s → C (insert a s)) : C s := sorry

theorem finite.dinduction_on {α : Type u} {C : (s : set α) → finite s → Prop} {s : set α} (h : finite s) (H0 : C ∅ finite_empty) (H1 : ∀ {a : α} {s : set α}, ¬a ∈ s → ∀ (h : finite s), C s h → C (insert a s) (finite.insert a h)) : C s h := sorry

protected instance fintype_singleton {α : Type u} (a : α) : fintype ↥(singleton a) :=
  unique.fintype

@[simp] theorem card_singleton {α : Type u} (a : α) : fintype.card ↥(singleton a) = 1 :=
  fintype.card_of_subsingleton Inhabited.default

@[simp] theorem finite_singleton {α : Type u} (a : α) : finite (singleton a) :=
  Nonempty.intro (set.fintype_singleton a)

protected instance fintype_pure {α : Type u} (a : α) : fintype ↥(pure a) :=
  set.fintype_singleton

theorem finite_pure {α : Type u} (a : α) : finite (pure a) :=
  Nonempty.intro (set.fintype_pure a)

protected instance fintype_univ {α : Type u} [fintype α] : fintype ↥univ :=
  fintype.of_equiv α (equiv.symm (equiv.set.univ α))

theorem finite_univ {α : Type u} [fintype α] : finite univ :=
  Nonempty.intro set.fintype_univ

theorem infinite_univ_iff {α : Type u} : infinite univ ↔ infinite α := sorry

theorem infinite_univ {α : Type u} [h : infinite α] : infinite univ :=
  iff.mpr infinite_univ_iff h

theorem infinite_coe_iff {α : Type u} {s : set α} : infinite ↥s ↔ infinite s := sorry

theorem infinite.to_subtype {α : Type u} {s : set α} (h : infinite s) : infinite ↥s :=
  iff.mpr infinite_coe_iff h

/-- Embedding of `ℕ` into an infinite set. -/
def infinite.nat_embedding {α : Type u} (s : set α) (h : infinite s) : ℕ ↪ ↥s :=
  infinite.nat_embedding ↥s

theorem infinite.exists_subset_card_eq {α : Type u} {s : set α} (hs : infinite s) (n : ℕ) : ∃ (t : finset α), ↑t ⊆ s ∧ finset.card t = n := sorry

protected instance fintype_union {α : Type u} [DecidableEq α] (s : set α) (t : set α) [fintype ↥s] [fintype ↥t] : fintype ↥(s ∪ t) :=
  fintype.of_finset (to_finset s ∪ to_finset t) sorry

theorem finite.union {α : Type u} {s : set α} {t : set α} : finite s → finite t → finite (s ∪ t) := sorry

protected instance fintype_sep {α : Type u} (s : set α) (p : α → Prop) [fintype ↥s] [decidable_pred p] : fintype ↥(has_sep.sep (fun (a : α) => p a) s) :=
  fintype.of_finset (finset.filter p (to_finset s)) sorry

protected instance fintype_inter {α : Type u} (s : set α) (t : set α) [fintype ↥s] [decidable_pred t] : fintype ↥(s ∩ t) :=
  set.fintype_sep s t

/-- A `fintype` structure on a set defines a `fintype` structure on its subset. -/
def fintype_subset {α : Type u} (s : set α) {t : set α} [fintype ↥s] [decidable_pred t] (h : t ⊆ s) : fintype ↥t :=
  eq.mpr sorry (set.fintype_inter s t)

theorem finite.subset {α : Type u} {s : set α} : finite s → ∀ {t : set α}, t ⊆ s → finite t :=
  fun (ᾰ : finite s) {t : set α} (ᾰ_1 : t ⊆ s) =>
    nonempty.dcases_on ᾰ fun (ᾰ_1_1 : fintype ↥s) => idRhs (Nonempty (fintype ↥t)) (Nonempty.intro (fintype_subset s ᾰ_1))

theorem infinite_mono {α : Type u} {s : set α} {t : set α} (h : s ⊆ t) : infinite s → infinite t :=
  mt fun (ht : finite t) => finite.subset ht h

protected instance fintype_image {α : Type u} {β : Type v} [DecidableEq β] (s : set α) (f : α → β) [fintype ↥s] : fintype ↥(f '' s) :=
  fintype.of_finset (finset.image f (to_finset s)) sorry

protected instance fintype_range {α : Type u} {β : Type v} [DecidableEq β] (f : α → β) [fintype α] : fintype ↥(range f) :=
  fintype.of_finset (finset.image f finset.univ) sorry

theorem finite_range {α : Type u} {β : Type v} (f : α → β) [fintype α] : finite (range f) :=
  Nonempty.intro (set.fintype_range f)

theorem finite.image {α : Type u} {β : Type v} {s : set α} (f : α → β) : finite s → finite (f '' s) :=
  fun (ᾰ : finite s) =>
    nonempty.dcases_on ᾰ
      fun (ᾰ : fintype ↥s) => idRhs (Nonempty (fintype ↥(f '' s))) (Nonempty.intro (set.fintype_image s f))

theorem infinite_of_infinite_image {α : Type u} {β : Type v} (f : α → β) {s : set α} (hs : infinite (f '' s)) : infinite s :=
  mt (finite.image f) hs

theorem finite.dependent_image {α : Type u} {β : Type v} {s : set α} (hs : finite s) {F : (i : α) → i ∈ s → β} {t : set β} (H : ∀ (y : β), y ∈ t → ∃ (x : α), ∃ (hx : x ∈ s), y = F x hx) : finite t := sorry

protected instance fintype_map {α : Type u_1} {β : Type u_1} [DecidableEq β] (s : set α) (f : α → β) [fintype ↥s] : fintype ↥(f <$> s) :=
  set.fintype_image

theorem finite.map {α : Type u_1} {β : Type u_1} {s : set α} (f : α → β) : finite s → finite (f <$> s) :=
  finite.image

/-- If a function `f` has a partial inverse and sends a set `s` to a set with `[fintype]` instance,
then `s` has a `fintype` structure as well. -/
def fintype_of_fintype_image {α : Type u} {β : Type v} (s : set α) {f : α → β} {g : β → Option α} (I : function.is_partial_inv f g) [fintype ↥(f '' s)] : fintype ↥s :=
  fintype.of_finset (finset.mk (multiset.filter_map g (finset.val (to_finset (f '' s)))) sorry) sorry

theorem finite_of_finite_image {α : Type u} {β : Type v} {s : set α} {f : α → β} (hi : inj_on f s) : finite (f '' s) → finite s := sorry

theorem finite_image_iff {α : Type u} {β : Type v} {s : set α} {f : α → β} (hi : inj_on f s) : finite (f '' s) ↔ finite s :=
  { mp := finite_of_finite_image hi, mpr := finite.image f }

theorem infinite_image_iff {α : Type u} {β : Type v} {s : set α} {f : α → β} (hi : inj_on f s) : infinite (f '' s) ↔ infinite s :=
  not_congr (finite_image_iff hi)

theorem infinite_of_inj_on_maps_to {α : Type u} {β : Type v} {s : set α} {t : set β} {f : α → β} (hi : inj_on f s) (hm : maps_to f s t) (hs : infinite s) : infinite t :=
  infinite_mono (iff.mp maps_to' hm) (iff.mpr (infinite_image_iff hi) hs)

theorem infinite_range_of_injective {α : Type u} {β : Type v} [infinite α] {f : α → β} (hi : function.injective f) : infinite (range f) := sorry

theorem infinite_of_injective_forall_mem {α : Type u} {β : Type v} [infinite α] {s : set β} {f : α → β} (hi : function.injective f) (hf : ∀ (x : α), f x ∈ s) : infinite s :=
  infinite_mono (eq.mp (Eq._oldrec (Eq.refl (∀ (x : α), f x ∈ s)) (Eq.symm (propext range_subset_iff))) hf)
    (infinite_range_of_injective hi)

theorem finite.preimage {α : Type u} {β : Type v} {s : set β} {f : α → β} (I : inj_on f (f ⁻¹' s)) (h : finite s) : finite (f ⁻¹' s) :=
  finite_of_finite_image I (finite.subset h (image_preimage_subset f s))

theorem finite.preimage_embedding {α : Type u} {β : Type v} {s : set β} (f : α ↪ β) (h : finite s) : finite (⇑f ⁻¹' s) := sorry

protected instance fintype_Union {α : Type u} [DecidableEq α] {ι : Type u_1} [fintype ι] (f : ι → set α) [(i : ι) → fintype ↥(f i)] : fintype ↥(Union fun (i : ι) => f i) :=
  fintype.of_finset (finset.bUnion finset.univ fun (i : ι) => to_finset (f i)) sorry

theorem finite_Union {α : Type u} {ι : Type u_1} [fintype ι] {f : ι → set α} (H : ∀ (i : ι), finite (f i)) : finite (Union fun (i : ι) => f i) :=
  Nonempty.intro (set.fintype_Union fun (i : ι) => f i)

/-- A union of sets with `fintype` structure over a set with `fintype` structure has a `fintype`
structure. -/
def fintype_set_bUnion {α : Type u} [DecidableEq α] {ι : Type u_1} {s : set ι} [fintype ↥s] (f : ι → set α) (H : (i : ι) → i ∈ s → fintype ↥(f i)) : fintype ↥(Union fun (i : ι) => Union fun (H : i ∈ s) => f i) :=
  eq.mpr sorry (set.fintype_Union fun (x : ↥s) => f ↑x)

protected instance fintype_set_bUnion' {α : Type u} [DecidableEq α] {ι : Type u_1} {s : set ι} [fintype ↥s] (f : ι → set α) [H : (i : ι) → fintype ↥(f i)] : fintype ↥(Union fun (i : ι) => Union fun (H : i ∈ s) => f i) :=
  fintype_set_bUnion (fun (i : ι) => f i) fun (i : ι) (_x : i ∈ s) => H i

theorem finite.sUnion {α : Type u} {s : set (set α)} (h : finite s) (H : ∀ (t : set α), t ∈ s → finite t) : finite (⋃₀s) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (finite (⋃₀s))) sUnion_eq_Union))
    (finite_Union (eq.mpr (id (propext set_coe.forall)) (eq.mp (Eq.refl (∀ (t : set α), t ∈ s → finite t)) H)))

theorem finite.bUnion {α : Type u_1} {ι : Type u_2} {s : set ι} {f : (i : ι) → i ∈ s → set α} : finite s → (∀ (i : ι) (H : i ∈ s), finite (f i H)) → finite (Union fun (i : ι) => Union fun (H : i ∈ s) => f i H) := sorry

protected instance fintype_lt_nat (n : ℕ) : fintype ↥(set_of fun (i : ℕ) => i < n) :=
  fintype.of_finset (finset.range n) sorry

protected instance fintype_le_nat (n : ℕ) : fintype ↥(set_of fun (i : ℕ) => i ≤ n) :=
  eq.mpr sorry (eq.mp sorry (set.fintype_lt_nat (n + 1)))

theorem finite_le_nat (n : ℕ) : finite (set_of fun (i : ℕ) => i ≤ n) :=
  Nonempty.intro (set.fintype_le_nat n)

theorem finite_lt_nat (n : ℕ) : finite (set_of fun (i : ℕ) => i < n) :=
  Nonempty.intro (set.fintype_lt_nat n)

protected instance fintype_prod {α : Type u} {β : Type v} (s : set α) (t : set β) [fintype ↥s] [fintype ↥t] : fintype ↥(set.prod s t) :=
  fintype.of_finset (finset.product (to_finset s) (to_finset t)) sorry

theorem finite.prod {α : Type u} {β : Type v} {s : set α} {t : set β} : finite s → finite t → finite (set.prod s t) := sorry

/-- `image2 f s t` is finitype if `s` and `t` are. -/
protected instance fintype_image2 {α : Type u} {β : Type v} {γ : Type x} [DecidableEq γ] (f : α → β → γ) (s : set α) (t : set β) [hs : fintype ↥s] [ht : fintype ↥t] : fintype ↥(image2 f s t) :=
  eq.mpr sorry (set.fintype_image (set.prod s t) fun (x : α × β) => f (prod.fst x) (prod.snd x))

theorem finite.image2 {α : Type u} {β : Type v} {γ : Type x} (f : α → β → γ) {s : set α} {t : set β} (hs : finite s) (ht : finite t) : finite (image2 f s t) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (finite (image2 f s t))) (Eq.symm (image_prod f))))
    (finite.image (fun (x : α × β) => f (prod.fst x) (prod.snd x)) (finite.prod hs ht))

/-- If `s : set α` is a set with `fintype` instance and `f : α → set β` is a function such that
each `f a`, `a ∈ s`, has a `fintype` structure, then `s >>= f` has a `fintype` structure. -/
def fintype_bUnion {α : Type u_1} {β : Type u_1} [DecidableEq β] (s : set α) [fintype ↥s] (f : α → set β) (H : (a : α) → a ∈ s → fintype ↥(f a)) : fintype ↥(s >>= f) :=
  fintype_set_bUnion (fun (i : α) => f i) H

protected instance fintype_bUnion' {α : Type u_1} {β : Type u_1} [DecidableEq β] (s : set α) [fintype ↥s] (f : α → set β) [H : (a : α) → fintype ↥(f a)] : fintype ↥(s >>= f) :=
  fintype_bUnion s f fun (i : α) (_x : i ∈ s) => H i

theorem finite_bUnion {α : Type u_1} {β : Type u_1} {s : set α} {f : α → set β} : finite s → (∀ (a : α), a ∈ s → finite (f a)) → finite (s >>= f) := sorry

protected instance fintype_seq {α : Type u} {β : Type u} [DecidableEq β] (f : set (α → β)) (s : set α) [fintype ↥f] [fintype ↥s] : fintype ↥(f <*> s) :=
  eq.mpr sorry (set.fintype_bUnion' f fun (_x : α → β) => _x <$> s)

theorem finite.seq {α : Type u} {β : Type u} {f : set (α → β)} {s : set α} : finite f → finite s → finite (f <*> s) := sorry

/-- There are finitely many subsets of a given finite set -/
theorem finite.finite_subsets {α : Type u} {a : set α} (h : finite a) : finite (set_of fun (b : set α) => b ⊆ a) := sorry

theorem exists_min_image {α : Type u} {β : Type v} [linear_order β] (s : set α) (f : α → β) (h1 : finite s) : set.nonempty s → ∃ (a : α), ∃ (H : a ∈ s), ∀ (b : α), b ∈ s → f a ≤ f b := sorry

theorem exists_max_image {α : Type u} {β : Type v} [linear_order β] (s : set α) (f : α → β) (h1 : finite s) : set.nonempty s → ∃ (a : α), ∃ (H : a ∈ s), ∀ (b : α), b ∈ s → f b ≤ f a := sorry

end set

namespace finset

theorem finite_to_set {α : Type u} (s : finset α) : set.finite ↑s :=
  set.finite_mem_finset s

@[simp] theorem coe_bUnion {α : Type u} {β : Type v} [DecidableEq β] {s : finset α} {f : α → finset β} : ↑(finset.bUnion s f) = set.Union fun (x : α) => set.Union fun (H : x ∈ ↑s) => ↑(f x) := sorry

@[simp] theorem finite_to_set_to_finset {α : Type u_1} (s : finset α) : set.finite.to_finset (finite_to_set s) = s := sorry

end finset

namespace set

theorem finite_subset_Union {α : Type u} {s : set α} (hs : finite s) {ι : Type u_1} {t : ι → set α} (h : s ⊆ Union fun (i : ι) => t i) : ∃ (I : set ι), finite I ∧ s ⊆ Union fun (i : ι) => Union fun (H : i ∈ I) => t i := sorry

theorem eq_finite_Union_of_finite_subset_Union {α : Type u} {ι : Type u_1} {s : ι → set α} {t : set α} (tfin : finite t) (h : t ⊆ Union fun (i : ι) => s i) : ∃ (I : set ι),
  finite I ∧
    ∃ (σ : ↥(set_of fun (i : ι) => i ∈ I) → set α),
      (∀ (i : ↥(set_of fun (i : ι) => i ∈ I)), finite (σ i)) ∧
        (∀ (i : ↥(set_of fun (i : ι) => i ∈ I)), σ i ⊆ s ↑i) ∧ t = Union fun (i : ↥(set_of fun (i : ι) => i ∈ I)) => σ i := sorry

/-- An increasing union distributes over finite intersection. -/
theorem Union_Inter_of_monotone {ι : Type u_1} {ι' : Type u_2} {α : Type u_3} [fintype ι] [linear_order ι'] [Nonempty ι'] {s : ι → ι' → set α} (hs : ∀ (i : ι), monotone (s i)) : (Union fun (j : ι') => Inter fun (i : ι) => s i j) = Inter fun (i : ι) => Union fun (j : ι') => s i j := sorry

protected instance nat.fintype_Iio (n : ℕ) : fintype ↥(Iio n) :=
  fintype.of_finset (finset.range n) sorry

/--
If `P` is some relation between terms of `γ` and sets in `γ`,
such that every finite set `t : set γ` has some `c : γ` related to it,
then there is a recursively defined sequence `u` in `γ`
so `u n` is related to the image of `{0, 1, ..., n-1}` under `u`.

(We use this later to show sequentially compact sets
are totally bounded.)
-/
theorem seq_of_forall_finite_exists {γ : Type u_1} {P : γ → set γ → Prop} (h : ∀ (t : set γ), finite t → ∃ (c : γ), P c t) : ∃ (u : ℕ → γ), ∀ (n : ℕ), P (u n) (u '' Iio n) := sorry

theorem finite_range_ite {α : Type u} {β : Type v} {p : α → Prop} [decidable_pred p] {f : α → β} {g : α → β} (hf : finite (range f)) (hg : finite (range g)) : finite (range fun (x : α) => ite (p x) (f x) (g x)) :=
  finite.subset (finite.union hf hg) range_ite_subset

theorem finite_range_const {α : Type u} {β : Type v} {c : β} : finite (range fun (x : α) => c) :=
  finite.subset (finite_singleton c) range_const_subset

theorem range_find_greatest_subset {α : Type u} {P : α → ℕ → Prop} [(x : α) → decidable_pred (P x)] {b : ℕ} : (range fun (x : α) => nat.find_greatest (P x) b) ⊆ ↑(finset.range (b + 1)) := sorry

theorem finite_range_find_greatest {α : Type u} {P : α → ℕ → Prop} [(x : α) → decidable_pred (P x)] {b : ℕ} : finite (range fun (x : α) => nat.find_greatest (P x) b) :=
  finite.subset (finset.finite_to_set (finset.range (b + 1))) range_find_greatest_subset

theorem card_lt_card {α : Type u} {s : set α} {t : set α} [fintype ↥s] [fintype ↥t] (h : s ⊂ t) : fintype.card ↥s < fintype.card ↥t := sorry

theorem card_le_of_subset {α : Type u} {s : set α} {t : set α} [fintype ↥s] [fintype ↥t] (hsub : s ⊆ t) : fintype.card ↥s ≤ fintype.card ↥t := sorry

theorem eq_of_subset_of_card_le {α : Type u} {s : set α} {t : set α} [fintype ↥s] [fintype ↥t] (hsub : s ⊆ t) (hcard : fintype.card ↥t ≤ fintype.card ↥s) : s = t :=
  or.elim (eq_or_ssubset_of_subset hsub) id fun (h : s ⊂ t) => absurd hcard (not_le_of_lt (card_lt_card h))

theorem subset_iff_to_finset_subset {α : Type u} (s : set α) (t : set α) [fintype ↥s] [fintype ↥t] : s ⊆ t ↔ to_finset s ⊆ to_finset t :=
  { mp := fun (h : s ⊆ t) (x : α) (hx : x ∈ to_finset s) => iff.mpr mem_to_finset (h (iff.mp mem_to_finset hx)),
    mpr :=
      fun (h : to_finset s ⊆ to_finset t) (x : α) (hx : x ∈ s) => iff.mp mem_to_finset (h (iff.mpr mem_to_finset hx)) }

theorem card_range_of_injective {α : Type u} {β : Type v} [fintype α] {f : α → β} (hf : function.injective f) [fintype ↥(range f)] : fintype.card ↥(range f) = fintype.card α :=
  Eq.symm (fintype.card_congr (equiv.set.range f hf))

theorem finite.exists_maximal_wrt {α : Type u} {β : Type v} [partial_order β] (f : α → β) (s : set α) (h : finite s) : set.nonempty s → ∃ (a : α), ∃ (H : a ∈ s), ∀ (a' : α), a' ∈ s → f a ≤ f a' → f a = f a' := sorry

theorem finite.card_to_finset {α : Type u} {s : set α} [fintype ↥s] (h : finite s) : finset.card (finite.to_finset h) = fintype.card ↥s := sorry

theorem to_finset_compl {α : Type u_1} [fintype α] (s : set α) : to_finset (sᶜ) = (to_finset sᶜ) := sorry

theorem to_finset_inter {α : Type u_1} [fintype α] (s : set α) (t : set α) : to_finset (s ∩ t) = to_finset s ∩ to_finset t := sorry

theorem to_finset_union {α : Type u_1} [fintype α] (s : set α) (t : set α) : to_finset (s ∪ t) = to_finset s ∪ to_finset t := sorry

/--A finite set is bounded above.-/
protected theorem finite.bdd_above {α : Type u} [semilattice_sup α] [Nonempty α] {s : set α} (hs : finite s) : bdd_above s :=
  finite.induction_on hs bdd_above_empty
    fun (a : α) (s : set α) (_x : ¬a ∈ s) (_x : finite s) (h : bdd_above s) => bdd_above.insert a h

/--A finite union of sets which are all bounded above is still bounded above.-/
theorem finite.bdd_above_bUnion {α : Type u} {β : Type v} [semilattice_sup α] [Nonempty α] {I : set β} {S : β → set α} (H : finite I) : bdd_above (Union fun (i : β) => Union fun (H : i ∈ I) => S i) ↔ ∀ (i : β), i ∈ I → bdd_above (S i) := sorry

/--A finite set is bounded below.-/
protected theorem finite.bdd_below {α : Type u} [semilattice_inf α] [Nonempty α] {s : set α} (hs : finite s) : bdd_below s :=
  finite.bdd_above hs

/--A finite union of sets which are all bounded below is still bounded below.-/
theorem finite.bdd_below_bUnion {α : Type u} {β : Type v} [semilattice_inf α] [Nonempty α] {I : set β} {S : β → set α} (H : finite I) : bdd_below (Union fun (i : β) => Union fun (H : i ∈ I) => S i) ↔ ∀ (i : β), i ∈ I → bdd_below (S i) :=
  finite.bdd_above_bUnion H

end set

namespace finset

/-- A finset is bounded above. -/
protected theorem bdd_above {α : Type u} [semilattice_sup α] [Nonempty α] (s : finset α) : bdd_above ↑s :=
  set.finite.bdd_above (finite_to_set s)

/-- A finset is bounded below. -/
protected theorem bdd_below {α : Type u} [semilattice_inf α] [Nonempty α] (s : finset α) : bdd_below ↑s :=
  set.finite.bdd_below (finite_to_set s)

end finset

theorem fintype.exists_max {α : Type u} [fintype α] [Nonempty α] {β : Type u_1} [linear_order β] (f : α → β) : ∃ (x₀ : α), ∀ (x : α), f x ≤ f x₀ := sorry


/-
Copyright (c) 2021 Yourong Zang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yourong Zang
-/
import analysis.complex.isometry
import analysis.normed_space.conformal_linear_map
import analysis.normed_space.finite_dimension

/-!
# Conformal maps between complex vector spaces

We prove the sufficient and necessary conditions for a real-linear map between complex vector spaces
to be conformal.

## Main results

* `is_conformal_map_complex_linear`: a nonzero complex linear map into an arbitrary complex
                                     normed space is conformal.
* `is_conformal_map_complex_linear_conj`: the composition of a nonzero complex linear map with
                                          `conj` is complex linear.
* `is_conformal_map_iff_is_complex_or_conj_linear`: a real linear map between the complex
                                                            plane is conformal iff it's complex
                                                            linear or the composition of
                                                            some complex linear map and `conj`.

## Warning

Antiholomorphic functions such as the complex conjugate are considered as conformal functions in
this file.
-/

noncomputable theory

open complex continuous_linear_map
open_locale complex_conjugate

lemma is_conformal_map_conj : is_conformal_map (conj_lie : ℂ →L[ℝ] ℂ) :=
conj_lie.to_linear_isometry.is_conformal_map

section conformal_into_complex_normed

variables {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] [normed_space ℂ E]
  {z : ℂ} {g : ℂ →L[ℝ] E} {f : ℂ → E}

lemma is_conformal_map_complex_linear {map : ℂ →L[ℂ] E} (nonzero : map ≠ 0) :
  is_conformal_map (map.restrict_scalars ℝ) :=
begin
  have minor₁ : ‖map 1‖ ≠ 0,
  { simpa only [ext_ring_iff, ne.def, norm_eq_zero] using nonzero},
  refine ⟨‖map 1‖, minor₁, ⟨‖map 1‖⁻¹ • map, _⟩, _⟩,
  { intros x,
    simp only [linear_map.smul_apply],
    have : x = x • 1 := by rw [smul_eq_mul, mul_one],
    nth_rewrite 0 [this],
    rw [_root_.coe_coe map, linear_map.coe_coe_is_scalar_tower],
    simp only [map.coe_coe, map.map_smul, norm_smul, norm_inv, norm_norm],
    field_simp only [one_mul] },
  { ext1,
    simp only [minor₁, linear_map.smul_apply, _root_.coe_coe, linear_map.coe_coe_is_scalar_tower,
      continuous_linear_map.coe_coe, coe_restrict_scalars', coe_smul',
      linear_isometry.coe_to_continuous_linear_map, linear_isometry.coe_mk, pi.smul_apply,
      smul_inv_smul₀, ne.def, not_false_iff] },
end

lemma is_conformal_map_complex_linear_conj
  {map : ℂ →L[ℂ] E} (nonzero : map ≠ 0) :
  is_conformal_map ((map.restrict_scalars ℝ).comp (conj_cle : ℂ →L[ℝ] ℂ)) :=
(is_conformal_map_complex_linear nonzero).comp is_conformal_map_conj

end conformal_into_complex_normed

section conformal_into_complex_plane

open continuous_linear_map

variables {f : ℂ → ℂ} {z : ℂ} {g : ℂ →L[ℝ] ℂ}

lemma is_conformal_map.is_complex_or_conj_linear (h : is_conformal_map g) :
  (∃ (map : ℂ →L[ℂ] ℂ), map.restrict_scalars ℝ = g) ∨
  (∃ (map : ℂ →L[ℂ] ℂ), map.restrict_scalars ℝ = g ∘L ↑conj_cle) :=
begin
  rcases h with ⟨c, hc, li, rfl⟩,
  obtain ⟨li, rfl⟩ : ∃ li' : ℂ ≃ₗᵢ[ℝ] ℂ, li'.to_linear_isometry = li,
    from ⟨li.to_linear_isometry_equiv rfl, by { ext1, refl }⟩,
  rcases linear_isometry_complex li with ⟨a, rfl|rfl⟩,
  -- let rot := c • (a : ℂ) • continuous_linear_map.id ℂ ℂ,
  { refine or.inl ⟨c • (a : ℂ) • continuous_linear_map.id ℂ ℂ, _⟩,
    ext1,
    simp only [coe_restrict_scalars', smul_apply, linear_isometry.coe_to_continuous_linear_map,
      linear_isometry_equiv.coe_to_linear_isometry, rotation_apply, id_apply, smul_eq_mul] },
  { refine or.inr ⟨c • (a : ℂ) • continuous_linear_map.id ℂ ℂ, _⟩,
    ext1,
    simp only [coe_restrict_scalars', smul_apply, linear_isometry.coe_to_continuous_linear_map,
      linear_isometry_equiv.coe_to_linear_isometry, rotation_apply, id_apply, smul_eq_mul,
      comp_apply, linear_isometry_equiv.trans_apply, continuous_linear_equiv.coe_coe,
      conj_cle_apply, conj_lie_apply, conj_conj] },
end

/-- A real continuous linear map on the complex plane is conformal if and only if the map or its
    conjugate is complex linear, and the map is nonvanishing. -/
lemma is_conformal_map_iff_is_complex_or_conj_linear:
  is_conformal_map g ↔
  ((∃ (map : ℂ →L[ℂ] ℂ), map.restrict_scalars ℝ = g) ∨
   (∃ (map : ℂ →L[ℂ] ℂ), map.restrict_scalars ℝ = g ∘L ↑conj_cle)) ∧ g ≠ 0 :=
begin
  split,
  { exact λ h, ⟨h.is_complex_or_conj_linear, h.ne_zero⟩, },
  { rintros ⟨⟨map, rfl⟩ | ⟨map, hmap⟩, h₂⟩,
    { refine is_conformal_map_complex_linear _,
      contrapose! h₂ with w,
      simp only [w, restrict_scalars_zero]},
    { have minor₁ : g = (map.restrict_scalars ℝ) ∘L ↑conj_cle,
      { ext1,
        simp only [hmap, coe_comp', continuous_linear_equiv.coe_coe, function.comp_app,
          conj_cle_apply, star_ring_end_self_apply]},
      rw minor₁ at ⊢ h₂,
      refine is_conformal_map_complex_linear_conj _,
      contrapose! h₂ with w,
      simp only [w, restrict_scalars_zero, zero_comp]} }
end

end conformal_into_complex_plane


/-
Copyright (c) 2021 Frédéric Dupuis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Frédéric Dupuis
-/

import algebra.star.basic
import group_theory.subgroup.basic

/-!
# Self-adjoint, skew-adjoint and normal elements of a star additive group

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

This file defines `self_adjoint R` (resp. `skew_adjoint R`), where `R` is a star additive group,
as the additive subgroup containing the elements that satisfy `star x = x` (resp. `star x = -x`).
This includes, for instance, (skew-)Hermitian operators on Hilbert spaces.

We also define `is_star_normal R`, a `Prop` that states that an element `x` satisfies
`star x * x = x * star x`.

## Implementation notes

* When `R` is a `star_module R₂ R`, then `self_adjoint R` has a natural
  `module (self_adjoint R₂) (self_adjoint R)` structure. However, doing this literally would be
  undesirable since in the main case of interest (`R₂ = ℂ`) we want `module ℝ (self_adjoint R)`
  and not `module (self_adjoint ℂ) (self_adjoint R)`. We solve this issue by adding the typeclass
  `[has_trivial_star R₃]`, of which `ℝ` is an instance (registered in `data/real/basic`), and then
  add a `[module R₃ (self_adjoint R)]` instance whenever we have
  `[module R₃ R] [has_trivial_star R₃]`. (Another approach would have been to define
  `[star_invariant_scalars R₃ R]` to express the fact that `star (x • v) = x • star v`, but
  this typeclass would have the disadvantage of taking two type arguments.)

## TODO

* Define `is_skew_adjoint` to match `is_self_adjoint`.
* Define `λ z x, z * x * star z` (i.e. conjugation by `z`) as a monoid action of `R` on `R`
  (similar to the existing `conj_act` for groups), and then state the fact that `self_adjoint R` is
  invariant under it.

-/

variables {R A : Type*}

/-- An element is self-adjoint if it is equal to its star. -/
def is_self_adjoint [has_star R] (x : R) : Prop := star x = x

/-- An element of a star monoid is normal if it commutes with its adjoint. -/
class is_star_normal [has_mul R] [has_star R] (x : R) : Prop :=
(star_comm_self : commute (star x) x)

export is_star_normal (star_comm_self)

lemma star_comm_self' [has_mul R] [has_star R] (x : R) [is_star_normal x] :
  (star x) * x = x * star x :=
is_star_normal.star_comm_self

namespace is_self_adjoint

-- named to match `commute.all`
/-- All elements are self-adjoint when `star` is trivial. -/
lemma all [has_star R] [has_trivial_star R] (r : R) : is_self_adjoint r := star_trivial _

lemma star_eq [has_star R] {x : R} (hx : is_self_adjoint x) : star x = x := hx

lemma _root_.is_self_adjoint_iff [has_star R] {x : R} : is_self_adjoint x ↔ star x = x := iff.rfl

@[simp]
lemma star_iff [has_involutive_star R] {x : R} : is_self_adjoint (star x) ↔ is_self_adjoint x :=
by simpa only [is_self_adjoint, star_star] using eq_comm

@[simp]
lemma star_mul_self [semigroup R] [star_semigroup R] (x : R) : is_self_adjoint (star x * x) :=
by simp only [is_self_adjoint, star_mul, star_star]

@[simp]
lemma mul_star_self [semigroup R] [star_semigroup R] (x : R) : is_self_adjoint (x * star x) :=
by simpa only [star_star] using star_mul_self (star x)

/-- Functions in a `star_hom_class` preserve self-adjoint elements. -/
lemma star_hom_apply {F R S : Type*} [has_star R] [has_star S] [star_hom_class F R S]
  {x : R} (hx : is_self_adjoint x) (f : F) : is_self_adjoint (f x) :=
show star (f x) = f x, from map_star f x ▸ congr_arg f hx

section add_monoid
variables [add_monoid R] [star_add_monoid R]

variables (R)

lemma _root_.is_self_adjoint_zero : is_self_adjoint (0 : R) := star_zero R

variables {R}

lemma add {x y : R} (hx : is_self_adjoint x) (hy : is_self_adjoint y) : is_self_adjoint (x + y) :=
by simp only [is_self_adjoint_iff, star_add, hx.star_eq, hy.star_eq]

lemma bit0 {x : R} (hx : is_self_adjoint x) : is_self_adjoint (bit0 x) :=
by simp only [is_self_adjoint_iff, star_bit0, hx.star_eq]

end add_monoid

section add_group
variables [add_group R] [star_add_monoid R]

lemma neg {x : R} (hx : is_self_adjoint x) : is_self_adjoint (-x) :=
by simp only [is_self_adjoint_iff, star_neg, hx.star_eq]

lemma sub {x y : R} (hx : is_self_adjoint x) (hy : is_self_adjoint y) : is_self_adjoint (x - y) :=
by simp only [is_self_adjoint_iff, star_sub, hx.star_eq, hy.star_eq]

end add_group

section add_comm_monoid
variables [add_comm_monoid R] [star_add_monoid R]

lemma _root_.is_self_adjoint_add_star_self (x : R) : is_self_adjoint (x + star x) :=
by simp only [is_self_adjoint_iff, add_comm, star_add, star_star]

lemma _root_.is_self_adjoint_star_add_self (x : R) : is_self_adjoint (star x + x) :=
by simp only [is_self_adjoint_iff, add_comm, star_add, star_star]

end add_comm_monoid

section semigroup
variables [semigroup R] [star_semigroup R]

lemma conjugate {x : R} (hx : is_self_adjoint x) (z : R) : is_self_adjoint (z * x * star z) :=
by simp only [is_self_adjoint_iff, star_mul, star_star, mul_assoc, hx.star_eq]

lemma conjugate' {x : R} (hx : is_self_adjoint x) (z : R) : is_self_adjoint (star z * x * z) :=
by simp only [is_self_adjoint_iff, star_mul, star_star, mul_assoc, hx.star_eq]

lemma is_star_normal {x : R} (hx : is_self_adjoint x) : is_star_normal x :=
⟨by simp only [hx.star_eq]⟩

end semigroup

section monoid
variables [monoid R] [star_semigroup R]

variables (R)

lemma _root_.is_self_adjoint_one : is_self_adjoint (1 : R) := star_one R

variables {R}

lemma pow {x : R} (hx : is_self_adjoint x) (n : ℕ) : is_self_adjoint (x ^ n):=
by simp only [is_self_adjoint_iff, star_pow, hx.star_eq]

end monoid

section semiring
variables [semiring R] [star_ring R]

lemma bit1 {x : R} (hx : is_self_adjoint x) : is_self_adjoint (bit1 x) :=
by simp only [is_self_adjoint_iff, star_bit1, hx.star_eq]

@[simp] lemma _root_.is_self_adjoint_nat_cast (n : ℕ) : is_self_adjoint (n : R) :=
star_nat_cast _

end semiring

section comm_semigroup
variables [comm_semigroup R] [star_semigroup R]

lemma mul {x y : R} (hx : is_self_adjoint x) (hy : is_self_adjoint y) : is_self_adjoint (x * y) :=
by simp only [is_self_adjoint_iff, star_mul', hx.star_eq, hy.star_eq]

end comm_semigroup

section ring
variables [ring R] [star_ring R]

@[simp] lemma _root_.is_self_adjoint_int_cast (z : ℤ) : is_self_adjoint (z : R) :=
star_int_cast _

end ring

section division_semiring
variables [division_semiring R] [star_ring R]

lemma inv {x : R} (hx : is_self_adjoint x) : is_self_adjoint x⁻¹ :=
by simp only [is_self_adjoint_iff, star_inv', hx.star_eq]

lemma zpow {x : R} (hx : is_self_adjoint x) (n : ℤ) : is_self_adjoint (x ^ n):=
by simp only [is_self_adjoint_iff, star_zpow₀, hx.star_eq]

end division_semiring

section division_ring
variables [division_ring R] [star_ring R]

lemma _root_.is_self_adjoint_rat_cast (x : ℚ) : is_self_adjoint (x : R) :=
star_rat_cast _

end division_ring

section semifield
variables [semifield R] [star_ring R]

lemma div {x y : R} (hx : is_self_adjoint x) (hy : is_self_adjoint y) : is_self_adjoint (x / y) :=
by simp only [is_self_adjoint_iff, star_div', hx.star_eq, hy.star_eq]

end semifield

section has_smul
variables [has_star R] [add_monoid A] [star_add_monoid A] [has_smul R A] [star_module R A]

lemma smul {r : R} (hr : is_self_adjoint r) {x : A} (hx : is_self_adjoint x) :
  is_self_adjoint (r • x) :=
by simp only [is_self_adjoint_iff, star_smul, hr.star_eq, hx.star_eq]

end has_smul

end is_self_adjoint

variables (R)

/-- The self-adjoint elements of a star additive group, as an additive subgroup. -/
def self_adjoint [add_group R] [star_add_monoid R] : add_subgroup R :=
{ carrier := {x | is_self_adjoint x},
  zero_mem' := star_zero R,
  add_mem' := λ _ _ hx, hx.add,
  neg_mem' := λ _ hx, hx.neg }

/-- The skew-adjoint elements of a star additive group, as an additive subgroup. -/
def skew_adjoint [add_comm_group R] [star_add_monoid R] : add_subgroup R :=
{ carrier := {x | star x = -x},
  zero_mem' := show star (0 : R) = -0, by simp only [star_zero, neg_zero],
  add_mem' := λ x y (hx : star x = -x) (hy : star y = -y),
                show star (x + y) = -(x + y), by rw [star_add x y, hx, hy, neg_add],
  neg_mem' := λ x (hx : star x = -x), show star (-x) = (- -x), by simp only [hx, star_neg] }

variables {R}

namespace self_adjoint

section add_group
variables [add_group R] [star_add_monoid R]

lemma mem_iff {x : R} : x ∈ self_adjoint R ↔ star x = x :=
by { rw [←add_subgroup.mem_carrier], exact iff.rfl }

@[simp, norm_cast] lemma star_coe_eq {x : self_adjoint R} : star (x : R) = x := x.prop

instance : inhabited (self_adjoint R) := ⟨0⟩

end add_group

section ring
variables [ring R] [star_ring R]

instance : has_one (self_adjoint R) := ⟨⟨1, is_self_adjoint_one R⟩⟩

@[simp, norm_cast] lemma coe_one : ↑(1 : self_adjoint R) = (1 : R) := rfl

instance [nontrivial R] : nontrivial (self_adjoint R) := ⟨⟨0, 1, subtype.ne_of_val_ne zero_ne_one⟩⟩

instance : has_nat_cast (self_adjoint R) :=
⟨λ n, ⟨n, is_self_adjoint_nat_cast _⟩⟩

instance : has_int_cast (self_adjoint R) :=
⟨λ n, ⟨n, is_self_adjoint_int_cast _⟩ ⟩

instance : has_pow (self_adjoint R) ℕ :=
⟨λ x n, ⟨(x : R) ^ n, x.prop.pow n⟩⟩

@[simp, norm_cast] lemma coe_pow (x : self_adjoint R) (n : ℕ) : ↑(x ^ n) = (x : R) ^ n := rfl

end ring

section non_unital_comm_ring
variables [non_unital_comm_ring R] [star_ring R]

instance : has_mul (self_adjoint R) :=
⟨λ x y, ⟨(x : R) * y, x.prop.mul y.prop⟩⟩

@[simp, norm_cast] lemma coe_mul (x y : self_adjoint R) : ↑(x * y) = (x : R) * y := rfl

end non_unital_comm_ring

section comm_ring
variables [comm_ring R] [star_ring R]

instance : comm_ring (self_adjoint R) :=
function.injective.comm_ring _ subtype.coe_injective
  (self_adjoint R).coe_zero coe_one (self_adjoint R).coe_add coe_mul (self_adjoint R).coe_neg
  (self_adjoint R).coe_sub (self_adjoint R).coe_nsmul (self_adjoint R).coe_zsmul coe_pow
  (λ _, rfl) (λ _, rfl)

end comm_ring

section field

variables [field R] [star_ring R]

instance : has_inv (self_adjoint R) :=
{ inv := λ x, ⟨(x.val)⁻¹, x.prop.inv⟩ }

@[simp, norm_cast] lemma coe_inv (x : self_adjoint R) : ↑(x⁻¹) = (x : R)⁻¹ := rfl

instance : has_div (self_adjoint R) :=
{ div := λ x y, ⟨x / y, x.prop.div y.prop⟩ }

@[simp, norm_cast] lemma coe_div (x y : self_adjoint R) : ↑(x / y) = (x / y : R) := rfl

instance : has_pow (self_adjoint R) ℤ :=
{ pow := λ x z, ⟨x ^ z, x.prop.zpow z⟩ }

@[simp, norm_cast] lemma coe_zpow (x : self_adjoint R) (z : ℤ) : ↑(x ^ z) = (x : R) ^ z := rfl

instance : has_rat_cast (self_adjoint R) :=
⟨λ n, ⟨n, is_self_adjoint_rat_cast n⟩⟩

@[simp, norm_cast] lemma coe_rat_cast (x : ℚ) : ↑(x : self_adjoint R) = (x : R) :=
rfl

instance has_qsmul : has_smul ℚ (self_adjoint R) :=
⟨λ a x, ⟨a • x, by rw rat.smul_def; exact is_self_adjoint.mul (is_self_adjoint_rat_cast a) x.prop⟩⟩

@[simp, norm_cast]

instance : field (self_adjoint R) :=
function.injective.field _ subtype.coe_injective
  (self_adjoint R).coe_zero coe_one (self_adjoint R).coe_add coe_mul (self_adjoint R).coe_neg
  (self_adjoint R).coe_sub coe_inv coe_div (self_adjoint R).coe_nsmul (self_adjoint R).coe_zsmul
  coe_rat_smul coe_pow coe_zpow (λ _, rfl) (λ _, rfl) coe_rat_cast

end field

section has_smul
variables [has_star R] [has_trivial_star R] [add_group A] [star_add_monoid A]

instance [has_smul R A] [star_module R A] : has_smul R (self_adjoint A) :=
⟨λ r x, ⟨r • x, (is_self_adjoint.all _).smul x.prop⟩⟩

@[simp, norm_cast] lemma coe_smul [has_smul R A] [star_module R A] (r : R) (x : self_adjoint A) :
  ↑(r • x) = r • (x : A) := rfl

instance [monoid R] [mul_action R A] [star_module R A] : mul_action R (self_adjoint A) :=
function.injective.mul_action coe subtype.coe_injective coe_smul

instance [monoid R] [distrib_mul_action R A] [star_module R A] :
  distrib_mul_action R (self_adjoint A) :=
function.injective.distrib_mul_action (self_adjoint A).subtype subtype.coe_injective coe_smul

end has_smul

section module
variables [has_star R] [has_trivial_star R] [add_comm_group A] [star_add_monoid A]

instance [semiring R] [module R A] [star_module R A] : module R (self_adjoint A) :=
function.injective.module R (self_adjoint A).subtype subtype.coe_injective coe_smul

end module

end self_adjoint

namespace skew_adjoint

section add_group
variables [add_comm_group R] [star_add_monoid R]

lemma mem_iff {x : R} : x ∈ skew_adjoint R ↔ star x = -x :=
by { rw [←add_subgroup.mem_carrier], exact iff.rfl }

@[simp, norm_cast] lemma star_coe_eq {x : skew_adjoint R} : star (x : R) = -x := x.prop

instance : inhabited (skew_adjoint R) := ⟨0⟩

lemma bit0_mem {x : R} (hx : x ∈ skew_adjoint R) : bit0 x ∈ skew_adjoint R :=
by rw [mem_iff, star_bit0, mem_iff.mp hx, bit0, bit0, neg_add]

end add_group

section ring
variables [ring R] [star_ring R]

lemma conjugate {x : R} (hx : x ∈ skew_adjoint R) (z : R) : z * x * star z ∈ skew_adjoint R :=
by simp only [mem_iff, star_mul, star_star, mem_iff.mp hx, neg_mul, mul_neg, mul_assoc]

lemma conjugate' {x : R} (hx : x ∈ skew_adjoint R) (z : R) : star z * x * z ∈ skew_adjoint R :=
by simp only [mem_iff, star_mul, star_star, mem_iff.mp hx, neg_mul, mul_neg, mul_assoc]

lemma is_star_normal_of_mem {x : R} (hx : x ∈ skew_adjoint R) : is_star_normal x :=
⟨by { simp only [mem_iff] at hx, simp only [hx, commute.neg_left] }⟩

instance (x : skew_adjoint R) : is_star_normal (x : R) :=
is_star_normal_of_mem (set_like.coe_mem _)

end ring

section has_smul
variables [has_star R] [has_trivial_star R] [add_comm_group A] [star_add_monoid A]

lemma smul_mem [monoid R] [distrib_mul_action R A] [star_module R A] (r : R) {x : A}
  (h : x ∈ skew_adjoint A) : r • x ∈ skew_adjoint A :=
by rw [mem_iff, star_smul, star_trivial, mem_iff.mp h, smul_neg r]

instance [monoid R] [distrib_mul_action R A] [star_module R A] : has_smul R (skew_adjoint A) :=
⟨λ r x, ⟨r • x, smul_mem r x.prop⟩⟩

@[simp, norm_cast] lemma coe_smul [monoid R] [distrib_mul_action R A] [star_module R A]
  (r : R) (x : skew_adjoint A) : ↑(r • x) = r • (x : A) := rfl

instance [monoid R] [distrib_mul_action R A] [star_module R A] :
  distrib_mul_action R (skew_adjoint A) :=
function.injective.distrib_mul_action (skew_adjoint A).subtype subtype.coe_injective coe_smul

instance [semiring R] [module R A] [star_module R A] : module R (skew_adjoint A) :=
function.injective.module R (skew_adjoint A).subtype subtype.coe_injective coe_smul

end has_smul

end skew_adjoint

/-- Scalar multiplication of a self-adjoint element by a skew-adjoint element produces a
skew-adjoint element. -/
lemma is_self_adjoint.smul_mem_skew_adjoint [ring R] [add_comm_group A] [module R A]
  [star_add_monoid R] [star_add_monoid A] [star_module R A] {r : R}
  (hr : r ∈ skew_adjoint R) {a : A} (ha : is_self_adjoint a) :
  r • a ∈ skew_adjoint A :=
(star_smul _ _).trans $ (congr_arg2 _ hr ha).trans $ neg_smul _ _

/-- Scalar multiplication of a skew-adjoint element by a skew-adjoint element produces a
self-adjoint element. -/
lemma is_self_adjoint_smul_of_mem_skew_adjoint [ring R] [add_comm_group A] [module R A]
  [star_add_monoid R] [star_add_monoid A] [star_module R A] {r : R}
  (hr : r ∈ skew_adjoint R) {a : A} (ha : a ∈ skew_adjoint A) :
  is_self_adjoint (r • a) :=
(star_smul _ _).trans $ (congr_arg2 _ hr ha).trans $ neg_smul_neg _ _

instance is_star_normal_zero [semiring R] [star_ring R] : is_star_normal (0 : R) :=
⟨by simp only [star_comm_self, star_zero]⟩

instance is_star_normal_one [monoid R] [star_semigroup R] : is_star_normal (1 : R) :=
⟨by simp only [star_comm_self, star_one]⟩

instance is_star_normal_star_self [monoid R] [star_semigroup R] {x : R} [is_star_normal x] :
  is_star_normal (star x) :=
⟨show star (star x) * (star x) = (star x) * star (star x), by rw [star_star, star_comm_self']⟩

@[priority 100] -- see Note [lower instance priority]
instance has_trivial_star.is_star_normal [monoid R] [star_semigroup R]
  [has_trivial_star R] {x : R} : is_star_normal x :=
⟨by rw [star_trivial]⟩

@[priority 100] -- see Note [lower instance priority]
instance comm_monoid.is_star_normal [comm_monoid R] [star_semigroup R] {x : R} :
  is_star_normal x :=
⟨mul_comm _ _⟩


-- CNS de divisible por cero
-- =========================

import tactic
import algebra.divisibility

variable (a : ℤ)

-- ----------------------------------------------------
-- Ejercicio. Demostrar que
--     0 ∣ a ↔ a = 0
-- ----------------------------------------------------

-- 1ª demostración
example : 0 ∣ a ↔ a = 0 :=
begin
  -- unfold has_dvd.dvd,
  split,
  { intro h,
    cases h with k hk,
    rw hk,
    rw zero_mul, },
  { intro h,
    use 0,
    rw h,
    rw zero_mul, },
end

-- 2ª demostración
example : 0 ∣ a ↔ a = 0 :=
begin
  split,
  { intro h,
    rcases h with ⟨k, rfl⟩,
    rw zero_mul, },
  { intro h,
    rw h, },
end

-- 3ª demostración
example : 0 ∣ a ↔ a = 0 :=
begin
  split,
  { rintro ⟨k, rfl⟩,
    ring, },
  { rintro rfl,
    use 0,
    ring, },
end

-- 4ª demostración
example : 0 ∣ a ↔ a = 0 :=
iff.intro
  ( assume h: 0 ∣ a,
    show a = 0, from
      dvd.elim h
        ( assume k,
          assume hk : a = 0 * k,
          show a = 0, from
            calc a = 0 * k : hk
               ... = 0     : zero_mul k))
  ( assume h : a = 0,
    have h1 : 0 * 0 = a, from
      calc 0 * 0 = 0 : zero_mul 0
             ... = a : by rw ← h,
    show 0 ∣ a, from dvd.intro 0 h1)

-- 5ª demostración
example : 0 ∣ a ↔ a = 0 :=
iff.intro
  ( assume h: 0 ∣ a,
    show a = 0, from
      dvd.elim h
        ( assume k,
          assume hk : a = 0 * k,
          show a = 0, from
            calc a = 0 * k : hk
               ... = 0     : zero_mul k))
  ( assume h : a = 0,
    show 0 ∣ a,
      from by rw h)

-- 6ª demostración
example : 0 ∣ a ↔ a = 0 :=
iff.intro
  ( assume h: 0 ∣ a,
    show a = 0, from
      dvd.elim h
        ( assume k,
          assume hk : a = 0 * k,
          show a = 0,
            from by rw [hk, zero_mul]))
  ( assume h : a = 0,
    show 0 ∣ a,
      from by rw h)

-- 7ª demostración
example : 0 ∣ a ↔ a = 0 :=
iff.intro
  (λ h, dvd.elim h (λ k hk, by rw [hk, zero_mul]))
  (λ h, by rw h)

-- 8ª demostración
example : 0 ∣ a ↔ a = 0 :=
⟨λ h, dvd.elim h (λ k hk, by rw [hk, zero_mul]),
 λ h, by rw h⟩

-- 9ª demostración
example : 0 ∣ a ↔ a = 0 :=
-- by library_search
zero_dvd_iff

-- 8ª demostración
example : 0 ∣ a ↔ a = 0 :=
-- by hint
by norm_num

-- 10ª demostración
example : 0 ∣ a ↔ a = 0 :=
by finish

-- 11ª demostración
example : 0 ∣ a ↔ a = 0 :=
by simp


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

! This file was ported from Lean 3 source module ring_theory.mv_polynomial.tower
! leanprover-community/mathlib commit bb168510ef455e9280a152e7f31673cabd3d7496
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Algebra.Tower
import Mathlib.Data.MvPolynomial.Basic

/-!
# Algebra towers for multivariate polynomial

This file proves some basic results about the algebra tower structure for the type
`MvPolynomial σ R`.

This structure itself is provided elsewhere as `MvPolynomial.isScalarTower`

When you update this file, you can also try to make a corresponding update in
`RingTheory.Polynomial.Tower`.
-/

variable (R A B : Type _) {σ : Type _}

namespace MvPolynomial

section Semiring

variable [CommSemiring R] [CommSemiring A] [CommSemiring B]

variable [Algebra R A] [Algebra A B] [Algebra R B]

variable [IsScalarTower R A B]

variable {R B}

theorem aeval_map_algebraMap (x : σ → B) (p : MvPolynomial σ R) :
    aeval x (map (algebraMap R A) p) = aeval x p := by
  rw [aeval_def, aeval_def, eval₂_map, IsScalarTower.algebraMap_eq R A B]
#align mv_polynomial.aeval_map_algebra_map MvPolynomial.aeval_map_algebraMap

end Semiring

section CommSemiring

variable [CommSemiring R] [CommSemiring A] [CommSemiring B]

variable [Algebra R A] [Algebra A B] [Algebra R B] [IsScalarTower R A B]

variable {R A}

theorem aeval_algebraMap_apply (x : σ → A) (p : MvPolynomial σ R) :
    aeval (algebraMap A B ∘ x) p = algebraMap A B (MvPolynomial.aeval x p) := by
  rw [aeval_def, aeval_def, ← coe_eval₂Hom, ← coe_eval₂Hom, map_eval₂Hom, ←
    IsScalarTower.algebraMap_eq]
  -- Porting note: added
  simp only [Function.comp]
#align mv_polynomial.aeval_algebra_map_apply MvPolynomial.aeval_algebraMap_apply

theorem aeval_algebraMap_eq_zero_iff [NoZeroSMulDivisors A B] [Nontrivial B] (x : σ → A)
    (p : MvPolynomial σ R) : aeval (algebraMap A B ∘ x) p = 0 ↔ aeval x p = 0 := by
  rw [aeval_algebraMap_apply, Algebra.algebraMap_eq_smul_one, smul_eq_zero,
    iff_false_intro (one_ne_zero' B), or_false_iff]
#align mv_polynomial.aeval_algebra_map_eq_zero_iff MvPolynomial.aeval_algebraMap_eq_zero_iff

theorem aeval_algebraMap_eq_zero_iff_of_injective {x : σ → A} {p : MvPolynomial σ R}
    (h : Function.Injective (algebraMap A B)) : aeval (algebraMap A B ∘ x) p = 0 ↔ aeval x p = 0 :=
  by rw [aeval_algebraMap_apply, ← (algebraMap A B).map_zero, h.eq_iff]
#align mv_polynomial.aeval_algebra_map_eq_zero_iff_of_injective MvPolynomial.aeval_algebraMap_eq_zero_iff_of_injective

end CommSemiring

end MvPolynomial

namespace Subalgebra

open MvPolynomial

section CommSemiring

variable {R A} [CommSemiring R] [CommSemiring A] [Algebra R A]

@[simp]

end CommSemiring

end Subalgebra


/-
Copyright (c) 2018 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

We isolate the first-order fragment of hol over some basic sorts. Assume sorts are numbered
0, 1, ..., n-1 for some n. (We should change that later.)
-/

/-
This will be convenient: a hol "arity" is of the form
  a1 ⇒ a2 ⇒ ... ⇒ an ⇒ r
where the `a`'s are sorts and the `r` is either a sort or `prop`. If `r` is a sort, the arity
is a function arity, and if `r` is `prop`, it is a relation arity
-/
import ..syntax

namespace hol

namespace arity
  @[derive has_reflect, derive decidable_eq]
  inductive kind
  | fn | rel | none

  namespace kind
    def repr : kind → string
    | fn := "fn"
    | rel := "rel"
    | none := "none"

    instance : has_repr kind := ⟨repr⟩

  end kind
end arity

namespace type

def is_sort (num_sorts : nat) : type → bool
| (type.Basic (type.basic.user n)) := if n < num_sorts then tt else ff
| _                                := ff

def is_prop : type → bool
| (type.Basic type.basic.prop) := tt
| _                            := ff

lemma is_not_arrow_of_is_sort {ty : type} {n : nat} : ty.is_sort n = tt → ty.is_arrow = ff :=
by cases ty; simp [type.is_sort]

section
  open arity

  def infer_arity_kind (num_sorts : nat) : type → arity.kind
  | (type.Var n)             := kind.none
  | t@(type.Basic _)         := if is_sort num_sorts t = tt then kind.fn
                                else if is_prop t = tt then kind.rel
                                else kind.none
  | (type.Arr t₁ t₂)         := if is_sort num_sorts t₁ = tt then infer_arity_kind t₂ else kind.none
  | (type.Constructor _)     := kind.none
  | (type.App _ _)           := kind.none
end

def is_arity (num_sorts : nat) (t : type) : bool :=
if t.infer_arity_kind num_sorts = arity.kind.none then ff else tt

theorem is_arity_iff (num_sorts : nat) (t : type) :
  is_arity num_sorts t = tt ↔
    ((∀ t' ∈ get_arg_types t, is_sort num_sorts t' = tt) ∧
      (is_sort num_sorts (get_return_type t) = tt ∨
      (is_prop (get_return_type t) = tt))) :=
begin
  induction t with _ _ t₁ t₂ ih₁ ih₂;
    simp [is_arity, infer_arity_kind, is_sort, is_prop] at *,
  { cases t; simp [is_sort, is_prop], by_cases h : t < num_sorts; simp [h] },
  by_cases h : is_sort num_sorts t₁ = tt; simp [h] at *,
  exact ih₂
end

/--
If `t` is an arity, `infer_arity num_sorts t` returns `some (alist, r)` where `alist` is the list of argument types, and `r` is the return type.

Otherwise, it return `none`.
-/
def infer_arity (num_sorts : nat) : type → option (list type × type)
| (type.Var n)             := none
| t@(type.Basic _)         := if is_sort num_sorts t || is_prop t = tt then some ([], t)
                              else none
| (type.Arr t₁ t₂)         := if is_sort num_sorts t₁ = tt then
                              match infer_arity t₂ with
                              | some (alist, r) := some (t₁ :: alist, r)
                              | none            := none
                              end
                              else none
| (type.Constructor _)     := none
| (type.App _ _)           := none

end type

end hol

namespace fol

-- TODO: probably better to use boolean bounded quantifiers
structure language :=
(num_sorts : nat)
(num_symbols : nat)
(arity : fin num_symbols → hol.type)
(arities_ok : ∀ i, (arity i).is_arity num_sorts = tt)

end fol

namespace hol

namespace type

@[reducible]
def is_fo_sort (L : fol.language) (t : type) : bool :=
t.is_sort L.num_sorts

def in_fo_language (L : fol.language) : type → bool
| (type.Var n)         := ff
| t@(type.Basic b)     := t.is_fo_sort L || t.is_prop
| (type.Arr t₁ t₂)     := in_fo_language t₁ && in_fo_language t₂
| (type.Constructor _) := ff
| (type.App _ _)       := ff

theorem is_in_language_of_is_sort (L : fol.language) :
    Π t : type, t.is_fo_sort L = tt → t.in_fo_language L = tt :=
by { intro t, cases t; simp [is_fo_sort, is_sort, in_fo_language], intro h, simp [h] }

theorem is_in_language_of_is_arity (L : fol.language) :
    Π t : type, t.is_arity L.num_sorts = tt → t.in_fo_language L = tt
| (type.Var n)         := by simp [is_arity, infer_arity_kind]
| (type.Basic b)       :=
    begin
      simp [is_arity, infer_arity_kind, in_fo_language, is_fo_sort],
      cases is_sort L.num_sorts (Basic b); simp,
      cases is_prop (Basic b); simp
    end
| (type.Arr t₁ t₂)     :=
    begin
      simp [in_fo_language, is_arity, infer_arity_kind],
      cases h: is_sort L.num_sorts t₁; simp,
      intro h₁, split,
      { apply is_in_language_of_is_sort _ _ h },
      apply is_in_language_of_is_arity, revert h₁,
      simp [is_arity]
    end
| (type.Constructor _) := by simp [is_arity, infer_arity_kind]
| (type.App _ _)       := by simp [is_arity, infer_arity_kind]

/- TODO: if we replace every `= tt` with a coercion to Prop, something fails in the
   previous lemma, and the terms are huge. -/

end type

namespace term

-- a hack for handling of quantifiers: fix later
def inhabited_type : inhabited type := ⟨mk_nat⟩
local attribute [instance] inhabited_type

namespace const

def in_fo_language (L : fol.language) : const → bool
| ⟨symb, t, l⟩ :=
  match symb with
  | (term.const.kind.user n)  := if h : n < L.num_symbols then
                                   if L.arity ⟨n, h⟩ = t ∧ t.in_fo_language L = bool.tt
                                   then l.empty else bool.ff
                                 else bool.ff
  | (term.const.kind.true)    := if t = mk_prop then l.empty else bool.ff
  | (term.const.kind.false)   := if t = mk_prop then l.empty else bool.ff
  | (term.const.kind.not)     := if t = (mk_prop ⇒ mk_prop) then l.empty else bool.ff
  | (term.const.kind.and)     := if t = (mk_prop ⇒ mk_prop ⇒ mk_prop) then l.empty else bool.ff
  | (term.const.kind.or)      := if t = (mk_prop ⇒ mk_prop ⇒ mk_prop) then l.empty else bool.ff
  | (term.const.kind.implies) := if t = (mk_prop ⇒ mk_prop ⇒ mk_prop) then l.empty else bool.ff
  | (term.const.kind.iff)     := if t = (mk_prop ⇒ mk_prop ⇒ mk_prop) then l.empty else bool.ff
  | (term.const.kind.all)     := let t' := l.head in
                                 if t = (t' ⇒ mk_prop ⇒ mk_prop) then bool.tt else bool.ff
                                  /- match l with
                                 | [t'] := if t = (t' ⇒ mk_prop ⇒ mk_prop) then bool.tt else bool.ff
                                 | _     := bool.ff
                                 end -/
  | (term.const.kind.ex)      := let t' := l.head in
                                 if t = (t' ⇒ mk_prop ⇒ mk_prop) then bool.tt else bool.ff
                                 /- match l with
                                 | [t'] := if t = (t' ⇒ mk_prop ⇒ mk_prop) then bool.tt else bool.ff
                                 | _     := bool.ff
                                 end -/
  | _                         := bool.ff
  end

end const

@[simp]
def in_fo_language (L : fol.language) (σ : list type): term → bool
| (term.Var n)      := if h : n < σ.length then (σ.nth_le n h).is_fo_sort L else bool.ff
| (term.Const c)    := c.in_fo_language L = tt
| (term.App t₁ t₂)  := in_fo_language t₁ && in_fo_language t₂ = tt
| (term.Abs s ty t) := ff

theorem is_sort_of_is_var {L : fol.language} {t : term} {σ : list type} :
  t.is_var = tt → in_fo_language L σ t = tt →
    (t.typeof σ).is_fo_sort L = tt :=
by { cases t with n; simp, by_cases h : n < σ.length; simp [h, typeof] }

theorem is_arity_of_is_const {L : fol.language} {t : term} {σ : list type} :
  t.is_const = tt → in_fo_language L σ t = tt →
      (t.typeof σ).get_return_type.is_sort L.num_sorts = tt →
    (t.typeof σ).is_arity L.num_sorts = tt :=
begin
  have h₀ : type.is_sort (L.num_sorts) (type.get_return_type mk_prop) = ff,
    reflexivity,
  induction t with _ c; simp,
  cases c; cases c_symb; simp [const.in_fo_language, const.in_fo_language._match_1, typeof],
  { by_cases h : c_symb < L.num_symbols; simp [h],
    intro heq, rw [← heq, L.arities_ok], simp },
  all_goals {
    try { intro heq; simp [heq, type.is_arity, type.get_return_type, h₀] } }
end

theorem in_fo_language_mk_app (L : fol.language) (t : term) (l : list term) (σ : list type) :
  (mk_app t l).in_fo_language L σ = tt ↔
    (t.in_fo_language L σ = tt ∧ (∀ t' ∈ l, in_fo_language L σ t' = tt)) :=
begin
  revert t, induction l with t' l ih; simp,
  intro t, rw ih, simp, rw [← and.assoc, @and.comm (_ = tt)],
end

theorem in_fo_language_get_app_fn {L : fol.language} {t : term} {σ : list type} :
  t.in_fo_language L σ = tt → (get_app_fn t).in_fo_language L σ = tt:=
begin
  induction t with _ _ _ _ ih; simp,
  intros h h', rw ih h,
end

theorem in_fo_language_cases {L : fol.language} {t : term} {σ : list type} :
  t.in_fo_language L σ = tt → t.is_const = tt ∨ t.is_var = tt ∨ t.is_app = tt :=
by { cases t; simp }

theorem is_const_get_app_fn_of_in_fo_language {L : fol.language} {t : term} {σ : list type}
    (h : in_fo_language L σ t = tt) :
  t.is_well_typed σ → t.is_app = tt → t.get_app_fn.is_const = tt :=
begin
  intros h₀ h₁,
  have := in_fo_language_cases (in_fo_language_get_app_fn h),
  cases this,
  { revert this, cases t; simp },
  cases this; revert this,
  { intro h',
    have h₂ := is_arrow_get_app_fn_of_is_app h₀ h₁,
    have h₃ := is_sort_of_is_var h' (in_fo_language_get_app_fn h),
    revert h₂ h₃,
    generalize : t.get_app_fn.typeof σ = T,
    cases T; simp [type.is_fo_sort, type.is_sort] },
  intro h',
  rw not_is_app_get_app_fn at h', contradiction
end

def is_fo_term (L : fol.language) (t : term) (σ : list type) : Prop :=
t.is_well_typed σ ∧ in_fo_language L σ t = tt ∧ (t.typeof σ).is_fo_sort L = tt

private theorem hoist_precondition {A B C : Prop} (h₀ : A → C) (h₁ : B → C) (h : C → (A ↔ B)) :
  A ↔ B :=
iff.intro (λ hA, (h (h₀ hA)).mp hA) (λ hB, (h (h₁ hB)).mpr hB)

theorem is_fo_term_iff_aux (L : fol.language) (t : term) (σ : list type) (h : t.is_app = tt):
  is_fo_term L t σ ↔
    let fn := t.get_app_fn,
          args := t.get_app_args,
          fn_type := fn.typeof σ in
        fn.in_fo_language L σ = tt ∧
        fn.is_const = tt ∧
        fn_type.get_return_type.is_fo_sort L = tt ∧
        (∀ t' ∈ args, is_fo_term L t' σ) ∧
        fn_type.get_arg_types = t.get_app_args.map (λ t', t'.typeof σ) :=
begin
  have : is_fo_term L t σ →  t.is_well_typed σ,
  { unfold is_fo_term, rintros ⟨h₁, h₂, h₃⟩, exact h₁},
  apply hoist_precondition this,
  { rintros ⟨h₁, h₂, h₃, h₄, h₅⟩,
    rw is_well_typed_iff',
    split, { apply is_well_typed_of_is_const h₂ },
    split, { intros t' ht', exact (h₄ _ ht').left },
    rw h₅ },
  intro is_well_typed_t,
  have : is_fo_term L t σ → (t.typeof σ).is_arrow = ff,
  { rintros ⟨h₁, h₂, h₃⟩, rw [type.is_not_arrow_of_is_sort h₃] },
  apply hoist_precondition this,
  { rintros ⟨h₁, h₂, h₃, h₄, h₅⟩,
    apply (type_is_not_arrow_iff is_well_typed_t).mpr,
    right, right, exact ⟨h, h₅⟩ },
  intro is_not_arrow_t,
  have in_fo_language_iff : in_fo_language L σ t = tt ↔
    in_fo_language L σ t.get_app_fn = tt ∧
      ∀ t' ∈ t.get_app_args, in_fo_language L σ t' = tt,
  { transitivity, { rw [← mk_app_get_app t, in_fo_language_mk_app] },
    apply iff.refl },
  have : is_fo_term L t σ → in_fo_language L σ t = tt,
  { unfold is_fo_term, rintros ⟨h₁, h₂, h₃⟩, exact h₂ },
  apply hoist_precondition this,
  { rintros ⟨h₁, h₂, h₃, h₄, h₅⟩, apply in_fo_language_iff.mpr,
    split, apply h₁, intros t' ht', apply (h₄ t' ht').right.left  },
  intro in_fo_language_t,
  have h' := is_const_get_app_fn_of_in_fo_language in_fo_language_t is_well_typed_t h,
  have h'' := (type_is_not_arrow' is_well_typed_t).mp is_not_arrow_t,
  have h''' := is_well_typed_iff'.mp is_well_typed_t,
  unfold is_fo_term,
  simp [is_well_typed_t, is_not_arrow_t, in_fo_language_t,
    in_fo_language_iff.mp in_fo_language_t, h', h''  ],
  rw ← typeof_eq_of_not_is_arrow is_not_arrow_t is_well_typed_t,
  split,
  { intro h'''', split, exact h'''',
    intros t' ht',
    split, exact h'''.right.left t' ht',
    split, apply (in_fo_language_iff.mp in_fo_language_t).right t' ht',
    have h₅ : t'.typeof σ ∈ type.get_arg_types (typeof (get_app_fn t) σ),
    { rw h'', apply list.mem_map_of_mem _ ht' },
    have : type.is_arity L.num_sorts (typeof (get_app_fn t) σ) = tt,
    { apply is_arity_of_is_const h' (in_fo_language_iff.mp in_fo_language_t).left,
      rw ← typeof_eq_of_not_is_arrow is_not_arrow_t is_well_typed_t,
      exact h'''' },
    have := ((type.is_arity_iff _ _).mp this).left _ h₅,
    exact this },
  exact and.left
end

theorem is_fo_term_iff (L : fol.language) (t : term) (σ : list type) :
  is_fo_term L t σ ↔
    (t.is_var = tt ∧ t.in_fo_language L σ = tt) ∨
    (t.is_const = tt ∧ t.in_fo_language L σ = tt ∧ (t.typeof σ).is_fo_sort L ) ∨
    (t.is_app = tt ∧
      let fn := t.get_app_fn,
          args := t.get_app_args,
          fn_type := fn.typeof σ in
         fn.in_fo_language L σ = tt ∧
         fn.is_const = tt ∧ fn_type.get_return_type.is_fo_sort L = tt ∧
        (∀ t' ∈ args, is_fo_term L t' σ) ∧
        fn_type.get_arg_types = t.get_app_args.map (λ t', t'.typeof σ)) :=
begin
  induction t,
  { simp [is_fo_term, in_fo_language, is_well_typed_iff, typeof],
    cases (decidable.em (t < list.length σ)) with h h; simp [h] },
  { simp [is_fo_term, in_fo_language, is_well_typed_iff] },
  { simp, apply is_fo_term_iff_aux, reflexivity  },
  simp [is_fo_term, type.is_fo_sort, type.is_sort, typeof]
end

end term

end hol

namespace fol

namespace language

theorem arity_in_fo_language (L : language) (i : fin L.num_symbols) :
  (L.arity i).in_fo_language L = tt :=
begin
  apply (L.arity i).is_in_language_of_is_arity L,
  apply L.arities_ok
end

end language

end fol