import tactic --hide

/-
## The `split` tactic

If your lemma has the goal:

```
⊢ P ∧ Q
```

then the  <mark style ="background-color : #ebdef0 ">`split`</mark>  tactic will turn it
into *two* goals `⊢ P` and `⊢ Q`.

-/

/-Hint : Tip
It is best practice to indicate when you are working with two goals, either by using squiggly
brackets like this:

```
...
split,
{ working on P,
  end of proof of P },
{ working on Q,
  end of proof of Q },
```

or by using indentation like this:

```
split,
  working on P,
  end of proof of P,
working on Q,
...
```
-/

/- Lemma
Let $P,Q$ be logical statements and assume that they are both true, then $P ∧ Q$ is
true.
-/
lemma split_example_one (P Q : Prop) (p : P) (q : Q) : P ∧ Q :=
begin
  split,
  exact p,
  exact q,
end


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Patrick Massot, Yury Kudryashov
-/
import algebra.group.opposite

/-!
# Monoid, group etc structures on `M × N`

In this file we define one-binop (`monoid`, `group` etc) structures on `M × N`. We also prove
trivial `simp` lemmas, and define the following operations on `monoid_hom`s:

* `fst M N : M × N →* M`, `snd M N : M × N →* N`: projections `prod.fst` and `prod.snd`
  as `monoid_hom`s;
* `inl M N : M →* M × N`, `inr M N : N →* M × N`: inclusions of first/second monoid
  into the product;
* `f.prod g : `M →* N × P`: sends `x` to `(f x, g x)`;
* `f.coprod g : M × N →* P`: sends `(x, y)` to `f x * g y`;
* `f.prod_map g : M × N → M' × N'`: `prod.map f g` as a `monoid_hom`,
  sends `(x, y)` to `(f x, g y)`.
-/

variables {A : Type*} {B : Type*} {G : Type*} {H : Type*} {M : Type*} {N : Type*} {P : Type*}

namespace prod

@[to_additive]
instance [has_mul M] [has_mul N] : has_mul (M × N) := ⟨λ p q, ⟨p.1 * q.1, p.2 * q.2⟩⟩

@[simp, to_additive]
lemma fst_mul [has_mul M] [has_mul N] (p q : M × N) : (p * q).1 = p.1 * q.1 := rfl
@[simp, to_additive]
lemma snd_mul [has_mul M] [has_mul N] (p q : M × N) : (p * q).2 = p.2 * q.2 := rfl
@[simp, to_additive]
lemma mk_mul_mk [has_mul M] [has_mul N] (a₁ a₂ : M) (b₁ b₂ : N) :
  (a₁, b₁) * (a₂, b₂) = (a₁ * a₂, b₁ * b₂) := rfl
@[to_additive]
lemma mul_def [has_mul M] [has_mul N] (p q : M × N) : p * q = (p.1 * q.1, p.2 * q.2) := rfl

@[to_additive]
instance [has_one M] [has_one N] : has_one (M × N) := ⟨(1, 1)⟩

@[simp, to_additive]
lemma fst_one [has_one M] [has_one N] : (1 : M × N).1 = 1 := rfl
@[simp, to_additive]
lemma snd_one [has_one M] [has_one N] : (1 : M × N).2 = 1 := rfl
@[to_additive]
lemma one_eq_mk [has_one M] [has_one N] : (1 : M × N) = (1, 1) := rfl
@[simp, to_additive]
lemma mk_eq_one [has_one M] [has_one N] {x : M} {y : N} : (x, y) = 1 ↔ x = 1 ∧ y = 1 :=
mk.inj_iff

@[to_additive]
lemma fst_mul_snd [mul_one_class M] [mul_one_class N] (p : M × N) :
  (p.fst, 1) * (1, p.snd) = p :=
ext (mul_one p.1) (one_mul p.2)

@[to_additive]
instance [has_inv M] [has_inv N] : has_inv (M × N) := ⟨λp, (p.1⁻¹, p.2⁻¹)⟩

@[simp, to_additive]
lemma fst_inv [has_inv G] [has_inv H] (p : G × H) : (p⁻¹).1 = (p.1)⁻¹ := rfl
@[simp, to_additive]
lemma snd_inv [has_inv G] [has_inv H] (p : G × H) : (p⁻¹).2 = (p.2)⁻¹ := rfl
@[simp, to_additive]
lemma inv_mk [has_inv G] [has_inv H] (a : G) (b : H) : (a, b)⁻¹ = (a⁻¹, b⁻¹) := rfl

@[to_additive]
instance [has_div M] [has_div N] : has_div (M × N) := ⟨λ p q, ⟨p.1 / q.1, p.2 / q.2⟩⟩

@[simp] lemma fst_sub [add_group A] [add_group B] (a b : A × B) : (a - b).1 = a.1 - b.1 := rfl
@[simp] lemma snd_sub [add_group A] [add_group B] (a b : A × B) : (a - b).2 = a.2 - b.2 := rfl
@[simp] lemma mk_sub_mk [add_group A] [add_group B] (x₁ x₂ : A) (y₁ y₂ : B) :
(x₁, y₁) - (x₂, y₂) = (x₁ - x₂, y₁ - y₂) := rfl

instance [mul_zero_class M] [mul_zero_class N] : mul_zero_class (M × N) :=
{ zero_mul := assume a, prod.rec_on a $ λa b, mk.inj_iff.mpr ⟨zero_mul _, zero_mul _⟩,
  mul_zero := assume a, prod.rec_on a $ λa b, mk.inj_iff.mpr ⟨mul_zero _, mul_zero _⟩,
  .. prod.has_zero, .. prod.has_mul }

@[to_additive]
instance [semigroup M] [semigroup N] : semigroup (M × N) :=
{ mul_assoc := assume a b c, mk.inj_iff.mpr ⟨mul_assoc _ _ _, mul_assoc _ _ _⟩,
  .. prod.has_mul }

instance [semigroup_with_zero M] [semigroup_with_zero N] : semigroup_with_zero (M × N) :=
{ .. prod.mul_zero_class, .. prod.semigroup }

@[to_additive]
instance [mul_one_class M] [mul_one_class N] : mul_one_class (M × N) :=
{ one_mul := assume a, prod.rec_on a $ λa b, mk.inj_iff.mpr ⟨one_mul _, one_mul _⟩,
  mul_one := assume a, prod.rec_on a $ λa b, mk.inj_iff.mpr ⟨mul_one _, mul_one _⟩,
  .. prod.has_mul, .. prod.has_one }

@[to_additive]
instance [monoid M] [monoid N] : monoid (M × N) :=
{ npow := λ z a, ⟨monoid.npow z a.1, monoid.npow z a.2⟩,
  npow_zero' := λ z, ext (monoid.npow_zero' _) (monoid.npow_zero' _),
  npow_succ' := λ z a, ext (monoid.npow_succ' _ _) (monoid.npow_succ' _ _),
  .. prod.semigroup, .. prod.mul_one_class }

@[to_additive]
instance [div_inv_monoid G] [div_inv_monoid H] : div_inv_monoid (G × H) :=
{ div_eq_mul_inv := λ a b, mk.inj_iff.mpr ⟨div_eq_mul_inv _ _, div_eq_mul_inv _ _⟩,
  zpow := λ z a, ⟨div_inv_monoid.zpow z a.1, div_inv_monoid.zpow z a.2⟩,
  zpow_zero' := λ z, ext (div_inv_monoid.zpow_zero' _) (div_inv_monoid.zpow_zero' _),
  zpow_succ' := λ z a, ext (div_inv_monoid.zpow_succ' _ _) (div_inv_monoid.zpow_succ' _ _),
  zpow_neg' := λ z a, ext (div_inv_monoid.zpow_neg' _ _) (div_inv_monoid.zpow_neg' _ _),
  .. prod.monoid, .. prod.has_inv, .. prod.has_div }

@[to_additive]
instance [group G] [group H] : group (G × H) :=
{ mul_left_inv := assume a, mk.inj_iff.mpr ⟨mul_left_inv _, mul_left_inv _⟩,
  .. prod.div_inv_monoid }

@[to_additive]
instance [comm_semigroup G] [comm_semigroup H] : comm_semigroup (G × H) :=
{ mul_comm := assume a b, mk.inj_iff.mpr ⟨mul_comm _ _, mul_comm _ _⟩,
  .. prod.semigroup }

@[to_additive]
instance [left_cancel_semigroup G] [left_cancel_semigroup H] :
  left_cancel_semigroup (G × H) :=
{ mul_left_cancel := λ a b c h, prod.ext (mul_left_cancel (prod.ext_iff.1 h).1)
    (mul_left_cancel (prod.ext_iff.1 h).2),
  .. prod.semigroup }

@[to_additive]
instance [right_cancel_semigroup G] [right_cancel_semigroup H] :
  right_cancel_semigroup (G × H) :=
{ mul_right_cancel := λ a b c h, prod.ext (mul_right_cancel (prod.ext_iff.1 h).1)
    (mul_right_cancel (prod.ext_iff.1 h).2),
  .. prod.semigroup }

@[to_additive]
instance [left_cancel_monoid M] [left_cancel_monoid N] : left_cancel_monoid (M × N) :=
{ .. prod.left_cancel_semigroup, .. prod.monoid }

@[to_additive]
instance [right_cancel_monoid M] [right_cancel_monoid N] : right_cancel_monoid (M × N) :=
{ .. prod.right_cancel_semigroup, .. prod.monoid }

@[to_additive]
instance [cancel_monoid M] [cancel_monoid N] : cancel_monoid (M × N) :=
{ .. prod.right_cancel_monoid, .. prod.left_cancel_monoid }

@[to_additive]
instance [comm_monoid M] [comm_monoid N] : comm_monoid (M × N) :=
{ .. prod.comm_semigroup, .. prod.monoid }

@[to_additive]
instance [cancel_comm_monoid M] [cancel_comm_monoid N] : cancel_comm_monoid (M × N) :=
{ .. prod.left_cancel_monoid, .. prod.comm_monoid }

instance [mul_zero_one_class M] [mul_zero_one_class N] : mul_zero_one_class (M × N) :=
{ .. prod.mul_zero_class, .. prod.mul_one_class }

instance [monoid_with_zero M] [monoid_with_zero N] : monoid_with_zero (M × N) :=
{ .. prod.monoid, .. prod.mul_zero_one_class }

instance [comm_monoid_with_zero M] [comm_monoid_with_zero N] : comm_monoid_with_zero (M × N) :=
{ .. prod.comm_monoid, .. prod.monoid_with_zero }

@[to_additive]
instance [comm_group G] [comm_group H] : comm_group (G × H) :=
{ .. prod.comm_semigroup, .. prod.group }

end prod

namespace monoid_hom

variables (M N) [mul_one_class M] [mul_one_class N]

/-- Given monoids `M`, `N`, the natural projection homomorphism from `M × N` to `M`.-/
@[to_additive "Given additive monoids `A`, `B`, the natural projection homomorphism
from `A × B` to `A`"]
def fst : M × N →* M := ⟨prod.fst, rfl, λ _ _, rfl⟩

/-- Given monoids `M`, `N`, the natural projection homomorphism from `M × N` to `N`.-/
@[to_additive "Given additive monoids `A`, `B`, the natural projection homomorphism
from `A × B` to `B`"]
def snd : M × N →* N := ⟨prod.snd, rfl, λ _ _, rfl⟩

/-- Given monoids `M`, `N`, the natural inclusion homomorphism from `M` to `M × N`. -/
@[to_additive "Given additive monoids `A`, `B`, the natural inclusion homomorphism
from `A` to `A × B`."]
def inl : M →* M × N :=
⟨λ x, (x, 1), rfl, λ _ _, prod.ext rfl (one_mul 1).symm⟩

/-- Given monoids `M`, `N`, the natural inclusion homomorphism from `N` to `M × N`. -/
@[to_additive "Given additive monoids `A`, `B`, the natural inclusion homomorphism
from `B` to `A × B`."]
def inr : N →* M × N :=
⟨λ y, (1, y), rfl, λ _ _, prod.ext (one_mul 1).symm rfl⟩

variables {M N}

@[simp, to_additive] lemma coe_fst : ⇑(fst M N) = prod.fst := rfl
@[simp, to_additive] lemma coe_snd : ⇑(snd M N) = prod.snd := rfl

@[simp, to_additive] lemma inl_apply (x) : inl M N x = (x, 1) := rfl
@[simp, to_additive] lemma inr_apply (y) : inr M N y = (1, y) := rfl

@[simp, to_additive] lemma fst_comp_inl : (fst M N).comp (inl M N) = id M := rfl
@[simp, to_additive] lemma snd_comp_inl : (snd M N).comp (inl M N) = 1 := rfl
@[simp, to_additive] lemma fst_comp_inr : (fst M N).comp (inr M N) = 1 := rfl
@[simp, to_additive] lemma snd_comp_inr : (snd M N).comp (inr M N) = id N := rfl

section prod

variable [mul_one_class P]

/-- Combine two `monoid_hom`s `f : M →* N`, `g : M →* P` into `f.prod g : M →* N × P`
given by `(f.prod g) x = (f x, g x)` -/
@[to_additive prod "Combine two `add_monoid_hom`s `f : M →+ N`, `g : M →+ P` into
`f.prod g : M →+ N × P` given by `(f.prod g) x = (f x, g x)`"]
protected def prod (f : M →* N) (g : M →* P) : M →* N × P :=
{ to_fun := λ x, (f x, g x),
  map_one' := prod.ext f.map_one g.map_one,
  map_mul' := λ x y, prod.ext (f.map_mul x y) (g.map_mul x y) }

@[simp, to_additive prod_apply]

@[simp, to_additive fst_comp_prod]
lemma fst_comp_prod (f : M →* N) (g : M →* P) : (fst N P).comp (f.prod g) = f :=
ext $ λ x, rfl

@[simp, to_additive snd_comp_prod]
lemma snd_comp_prod (f : M →* N) (g : M →* P) : (snd N P).comp (f.prod g) = g :=
ext $ λ x, rfl

@[simp, to_additive prod_unique]
lemma prod_unique (f : M →* N × P) :
  ((fst N P).comp f).prod ((snd N P).comp f) = f :=
ext $ λ x, by simp only [prod_apply, coe_fst, coe_snd, comp_apply, prod.mk.eta]

end prod

section prod_map

variables {M' : Type*} {N' : Type*} [mul_one_class M'] [mul_one_class N'] [mul_one_class P]
  (f : M →* M') (g : N →* N')

/-- `prod.map` as a `monoid_hom`. -/
@[to_additive prod_map "`prod.map` as an `add_monoid_hom`"]
def prod_map : M × N →* M' × N' := (f.comp (fst M N)).prod (g.comp (snd M N))

@[to_additive prod_map_def]
lemma prod_map_def : prod_map f g = (f.comp (fst M N)).prod (g.comp (snd M N)) := rfl

@[simp, to_additive coe_prod_map]
lemma coe_prod_map : ⇑(prod_map f g) = prod.map f g := rfl

@[to_additive prod_comp_prod_map]
lemma prod_comp_prod_map (f : P →* M) (g : P →* N) (f' : M →* M') (g' : N →* N') :
  (f'.prod_map g').comp (f.prod g) = (f'.comp f).prod (g'.comp g) :=
rfl

end prod_map

section coprod

variables [comm_monoid P] (f : M →* P) (g : N →* P)

/-- Coproduct of two `monoid_hom`s with the same codomain:
`f.coprod g (p : M × N) = f p.1 * g p.2`. -/
@[to_additive "Coproduct of two `add_monoid_hom`s with the same codomain:
`f.coprod g (p : M × N) = f p.1 + g p.2`."]
def coprod : M × N →* P := f.comp (fst M N) * g.comp (snd M N)

@[simp, to_additive]
lemma coprod_apply (p : M × N) : f.coprod g p = f p.1 * g p.2 := rfl

@[simp, to_additive]
lemma coprod_comp_inl : (f.coprod g).comp (inl M N) = f :=
ext $ λ x, by simp [coprod_apply]

@[simp, to_additive]
lemma coprod_comp_inr : (f.coprod g).comp (inr M N) = g :=
ext $ λ x, by simp [coprod_apply]

@[simp, to_additive] lemma coprod_unique (f : M × N →* P) :
  (f.comp (inl M N)).coprod (f.comp (inr M N)) = f :=
ext $ λ x, by simp [coprod_apply, inl_apply, inr_apply, ← map_mul]

@[simp, to_additive] lemma coprod_inl_inr {M N : Type*} [comm_monoid M] [comm_monoid N] :
  (inl M N).coprod (inr M N) = id (M × N) :=
coprod_unique (id $ M × N)

lemma comp_coprod {Q : Type*} [comm_monoid Q] (h : P →* Q) (f : M →* P) (g : N →* P) :
  h.comp (f.coprod g) = (h.comp f).coprod (h.comp g) :=
ext $ λ x, by simp

end coprod

end monoid_hom

namespace mul_equiv

section
variables {M N} [mul_one_class M] [mul_one_class N]

/-- The equivalence between `M × N` and `N × M` given by swapping the components
is multiplicative. -/
@[to_additive prod_comm "The equivalence between `M × N` and `N × M` given by swapping the
components is additive."]
def prod_comm : M × N ≃* N × M :=
{ map_mul' := λ ⟨x₁, y₁⟩ ⟨x₂, y₂⟩, rfl, ..equiv.prod_comm M N }

@[simp, to_additive coe_prod_comm] lemma coe_prod_comm :
  ⇑(prod_comm : M × N ≃* N × M) = prod.swap := rfl

@[simp, to_additive coe_prod_comm_symm] lemma coe_prod_comm_symm :
  ⇑((prod_comm : M × N ≃* N × M).symm) = prod.swap := rfl

end

section
variables {M N} [monoid M] [monoid N]

/-- The monoid equivalence between units of a product of two monoids, and the product of the
    units of each monoid. -/
@[to_additive prod_add_units "The additive monoid equivalence between additive units of a product
of two additive monoids, and the product of the additive units of each additive monoid."]
def prod_units : units (M × N) ≃* units M × units N :=
{ to_fun := (units.map (monoid_hom.fst M N)).prod (units.map (monoid_hom.snd M N)),
  inv_fun := λ u, ⟨(u.1, u.2), (↑u.1⁻¹, ↑u.2⁻¹), by simp, by simp⟩,
  left_inv := λ u, by simp,
  right_inv := λ ⟨u₁, u₂⟩, by simp [units.map],
  map_mul' := monoid_hom.map_mul _ }

end

end mul_equiv

section units

open mul_opposite

/-- Canonical homomorphism of monoids from `units α` into `α × αᵐᵒᵖ`.
Used mainly to define the natural topology of `units α`. -/
def embed_product (α : Type*) [monoid α] : units α →* α × αᵐᵒᵖ :=
{ to_fun := λ x, ⟨x, op ↑x⁻¹⟩,
  map_one' := by simp only [one_inv, eq_self_iff_true, units.coe_one, op_one, prod.mk_eq_one,
    and_self],
  map_mul' := λ x y, by simp only [mul_inv_rev, op_mul, units.coe_mul, prod.mk_mul_mk] }

end units


/-
Copyright (c) 2018 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Patrick Massot, Eric Wieser
-/
import algebra.group.prod
import group_theory.group_action.defs

/-!
# Prod instances for additive and multiplicative actions

This file defines instances for binary product of additive and multiplicative actions
-/

variables {M N P α β : Type*}

namespace prod

section

variables [has_scalar M α] [has_scalar M β] [has_scalar N α] [has_scalar N β] (a : M) (x : α × β)

@[to_additive prod.has_vadd] instance : has_scalar M (α × β) := ⟨λa p, (a • p.1, a • p.2)⟩

@[simp, to_additive] theorem smul_fst : (a • x).1 = a • x.1 := rfl
@[simp, to_additive] theorem smul_snd : (a • x).2 = a • x.2 := rfl
@[simp, to_additive] theorem smul_mk (a : M) (b : α) (c : β) : a • (b, c) = (a • b, a • c) := rfl
@[to_additive] theorem smul_def (a : M) (x : α × β) : a • x = (a • x.1, a • x.2) := rfl

instance [has_scalar M N] [is_scalar_tower M N α] [is_scalar_tower M N β] :
  is_scalar_tower M N (α × β) :=
⟨λ x y z, mk.inj_iff.mpr ⟨smul_assoc _ _ _, smul_assoc _ _ _⟩⟩

@[to_additive] instance [smul_comm_class M N α] [smul_comm_class M N β] :
  smul_comm_class M N (α × β) :=
{ smul_comm := λ r s x, mk.inj_iff.mpr ⟨smul_comm _ _ _, smul_comm _ _ _⟩ }

@[to_additive has_faithful_vadd_left]
instance has_faithful_scalar_left [has_faithful_scalar M α] [nonempty β] :
  has_faithful_scalar M (α × β) :=
⟨λ x y h, let ⟨b⟩ := ‹nonempty β› in eq_of_smul_eq_smul $ λ a : α, by injection h (a, b)⟩

@[to_additive has_faithful_vadd_right]
instance has_faithful_scalar_right [nonempty α] [has_faithful_scalar M β] :
  has_faithful_scalar M (α × β) :=
⟨λ x y h, let ⟨a⟩ := ‹nonempty α› in eq_of_smul_eq_smul $ λ b : β, by injection h (a, b)⟩

end

@[to_additive]
instance smul_comm_class_both [monoid N] [monoid P] [has_scalar M N] [has_scalar M P]
  [smul_comm_class M N N] [smul_comm_class M P P] :
  smul_comm_class M (N × P) (N × P) :=
⟨λ c x y, by simp [smul_def, mul_def, mul_smul_comm]⟩

instance is_scalar_tower_both [monoid N] [monoid P] [has_scalar M N] [has_scalar M P]
  [is_scalar_tower M N N] [is_scalar_tower M P P] :
  is_scalar_tower M (N × P) (N × P) :=
⟨λ c x y, by simp [smul_def, mul_def, smul_mul_assoc]⟩

@[to_additive] instance {m : monoid M} [mul_action M α] [mul_action M β] : mul_action M (α × β) :=
{ mul_smul  := λ a₁ a₂ p, mk.inj_iff.mpr ⟨mul_smul _ _ _, mul_smul _ _ _⟩,
  one_smul  := λ ⟨b, c⟩, mk.inj_iff.mpr ⟨one_smul _ _, one_smul _ _⟩ }

instance {R M N : Type*} {r : monoid R} [add_monoid M] [add_monoid N]
  [distrib_mul_action R M] [distrib_mul_action R N] : distrib_mul_action R (M × N) :=
{ smul_add  := λ a p₁ p₂, mk.inj_iff.mpr ⟨smul_add _ _ _, smul_add _ _ _⟩,
  smul_zero := λ a, mk.inj_iff.mpr ⟨smul_zero _, smul_zero _⟩ }

instance {R M N : Type*} {r : monoid R} [monoid M] [monoid N]
  [mul_distrib_mul_action R M] [mul_distrib_mul_action R N] : mul_distrib_mul_action R (M × N) :=
{ smul_mul  := λ a p₁ p₂, mk.inj_iff.mpr ⟨smul_mul' _ _ _, smul_mul' _ _ _⟩,
  smul_one := λ a, mk.inj_iff.mpr ⟨smul_one _, smul_one _⟩ }

end prod


/-
Copyright (c) Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.normed_space.finite_dimension
import Mathlib.data.complex.module
import Mathlib.data.complex.is_R_or_C
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Normed space structure on `ℂ`.

This file gathers basic facts on complex numbers of an analytic nature.

## Main results

This file registers `ℂ` as a normed field, expresses basic properties of the norm, and gives
tools on the real vector space structure of `ℂ`. Notably, in the namespace `complex`,
it defines functions:

* `continuous_linear_map.re`
* `continuous_linear_map.im`
* `continuous_linear_map.of_real`

They are bundled versions of the real part, the imaginary part, and the embedding of `ℝ` in `ℂ`,
as continuous `ℝ`-linear maps.

We also register the fact that `ℂ` is an `is_R_or_C` field.
-/

namespace complex

protected instance has_norm : has_norm ℂ := has_norm.mk abs

protected instance normed_group : normed_group ℂ := normed_group.of_core ℂ sorry

protected instance normed_field : normed_field ℂ := normed_field.mk sorry abs_mul

protected instance nondiscrete_normed_field : nondiscrete_normed_field ℂ :=
  nondiscrete_normed_field.mk sorry

protected instance normed_algebra_over_reals : normed_algebra ℝ ℂ := normed_algebra.mk abs_of_real

@[simp] theorem norm_eq_abs (z : ℂ) : norm z = abs z := rfl

theorem dist_eq (z : ℂ) (w : ℂ) : dist z w = abs (z - w) := rfl

@[simp] theorem norm_real (r : ℝ) : norm ↑r = norm r := abs_of_real r

@[simp] theorem norm_rat (r : ℚ) : norm ↑r = abs ↑r := sorry

@[simp] theorem norm_nat (n : ℕ) : norm ↑n = ↑n := abs_of_nat n

@[simp] theorem norm_int {n : ℤ} : norm ↑n = abs ↑n := sorry

theorem norm_int_of_nonneg {n : ℤ} (hn : 0 ≤ n) : norm ↑n = ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm ↑n = ↑n)) norm_int))
    (eq.mpr (id (Eq._oldrec (Eq.refl (abs ↑n = ↑n)) (abs_of_nonneg (iff.mpr int.cast_nonneg hn))))
      (Eq.refl ↑n))

/-- A complex normed vector space is also a real normed vector space. -/
protected instance normed_space.restrict_scalars_real (E : Type u_1) [normed_group E]
    [normed_space ℂ E] : normed_space ℝ E :=
  normed_space.restrict_scalars ℝ ℂ E

/-- The space of continuous linear maps over `ℝ`, from a real vector space to a complex vector
space, is a normed vector space over `ℂ`. -/
protected instance continuous_linear_map.real_smul_complex (E : Type u_1) [normed_group E]
    [normed_space ℝ E] (F : Type u_2) [normed_group F] [normed_space ℂ F] :
    normed_space ℂ (continuous_linear_map ℝ E F) :=
  continuous_linear_map.normed_space_extend_scalars

/-- Continuous linear map version of the real part function, from `ℂ` to `ℝ`. -/
def continuous_linear_map.re : continuous_linear_map ℝ ℂ ℝ :=
  linear_map.to_continuous_linear_map linear_map.re

@[simp] theorem continuous_linear_map.re_coe : ↑continuous_linear_map.re = linear_map.re := rfl

@[simp] theorem continuous_linear_map.re_apply (z : ℂ) : coe_fn continuous_linear_map.re z = re z :=
  rfl

@[simp] theorem continuous_linear_map.re_norm : norm continuous_linear_map.re = 1 := sorry

/-- Continuous linear map version of the real part function, from `ℂ` to `ℝ`. -/
def continuous_linear_map.im : continuous_linear_map ℝ ℂ ℝ :=
  linear_map.to_continuous_linear_map linear_map.im

@[simp] theorem continuous_linear_map.im_coe : ↑continuous_linear_map.im = linear_map.im := rfl

@[simp] theorem continuous_linear_map.im_apply (z : ℂ) : coe_fn continuous_linear_map.im z = im z :=
  rfl

@[simp] theorem continuous_linear_map.im_norm : norm continuous_linear_map.im = 1 := sorry

/-- Linear isometry version of the canonical embedding of `ℝ` in `ℂ`. -/
def linear_isometry.of_real : linear_isometry ℝ ℝ ℂ := linear_isometry.mk linear_map.of_real sorry

/-- Continuous linear map version of the canonical embedding of `ℝ` in `ℂ`. -/
def continuous_linear_map.of_real : continuous_linear_map ℝ ℝ ℂ :=
  linear_isometry.to_continuous_linear_map linear_isometry.of_real

theorem isometry_of_real : isometry coe := linear_isometry.isometry linear_isometry.of_real

theorem continuous_of_real : continuous coe := isometry.continuous isometry_of_real

@[simp] theorem continuous_linear_map.of_real_coe :
    ↑continuous_linear_map.of_real = linear_map.of_real :=
  rfl

@[simp] theorem continuous_linear_map.of_real_apply (x : ℝ) :
    coe_fn continuous_linear_map.of_real x = ↑x :=
  rfl

@[simp] theorem continuous_linear_map.of_real_norm : norm continuous_linear_map.of_real = 1 :=
  linear_isometry.norm_to_continuous_linear_map linear_isometry.of_real

protected instance is_R_or_C : is_R_or_C ℂ :=
  is_R_or_C.mk (add_monoid_hom.mk re zero_re add_re) (add_monoid_hom.mk im zero_im add_im) conj I
    sorry sorry sorry sorry sorry sorry sorry sorry sorry sorry sorry sorry sorry div_I

end complex

namespace is_R_or_C

@[simp] theorem re_to_complex {x : ℂ} : coe_fn re x = complex.re x := rfl

@[simp] theorem im_to_complex {x : ℂ} : coe_fn im x = complex.im x := rfl

@[simp] theorem conj_to_complex {x : ℂ} : coe_fn conj x = coe_fn complex.conj x := rfl

@[simp] theorem I_to_complex : I = complex.I := rfl

@[simp] theorem norm_sq_to_complex {x : ℂ} : coe_fn norm_sq x = coe_fn complex.norm_sq x := sorry

@[simp] theorem abs_to_complex {x : ℂ} : abs x = complex.abs x := sorry

end Mathlib


/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri

! This file was ported from Lean 3 source module geometry.manifold.cont_mdiff_map
! leanprover-community/mathlib commit be2c24f56783935652cefffb4bfca7e4b25d167e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Geometry.Manifold.ContMdiffMfderiv
import Mathbin.Topology.ContinuousFunction.Basic

/-!
# Smooth bundled map

In this file we define the type `cont_mdiff_map` of `n` times continuously differentiable
bundled maps.
-/

variable {𝕜 : Type _} [NontriviallyNormedField 𝕜] {E : Type _} [NormedAddCommGroup E]
  [NormedSpace 𝕜 E] {E' : Type _} [NormedAddCommGroup E'] [NormedSpace 𝕜 E'] {H : Type _}
  [TopologicalSpace H] {H' : Type _} [TopologicalSpace H'] (I : ModelWithCorners 𝕜 E H)
  (I' : ModelWithCorners 𝕜 E' H') (M : Type _) [TopologicalSpace M] [ChartedSpace H M] (M' : Type _)
  [TopologicalSpace M'] [ChartedSpace H' M'] {E'' : Type _} [NormedAddCommGroup E'']
  [NormedSpace 𝕜 E''] {H'' : Type _} [TopologicalSpace H''] {I'' : ModelWithCorners 𝕜 E'' H''}
  {M'' : Type _} [TopologicalSpace M''] [ChartedSpace H'' M''] (n : ℕ∞)

/-- Bundled `n` times continuously differentiable maps. -/
@[protect_proj]
structure ContMdiffMap where
  toFun : M → M'
  contMdiff_toFun : ContMdiff I I' n to_fun
#align cont_mdiff_map ContMdiffMap

/-- Bundled smooth maps. -/
@[reducible]
def SmoothMap :=
  ContMdiffMap I I' M M' ⊤
#align smooth_map SmoothMap

-- mathport name: cont_mdiff_map
scoped[Manifold] notation "C^" n "⟮" I ", " M "; " I' ", " M' "⟯" => ContMdiffMap I I' M M' n

-- mathport name: cont_mdiff_map.self
scoped[Manifold]
  notation "C^" n "⟮" I ", " M "; " k "⟯" => ContMdiffMap I (modelWithCornersSelf k k) M k n

open Manifold

namespace ContMdiffMap

variable {I} {I'} {M} {M'} {n}

instance : CoeFun C^n⟮I, M; I', M'⟯ fun _ => M → M' :=
  ⟨ContMdiffMap.toFun⟩

instance : Coe C^n⟮I, M; I', M'⟯ C(M, M') :=
  ⟨fun f => ⟨f, f.contMdiff_toFun.Continuous⟩⟩

attribute [to_additive_ignore_args 21]
  ContMdiffMap ContMdiffMap.hasCoeToFun ContMdiffMap.ContinuousMap.hasCoe

variable {f g : C^n⟮I, M; I', M'⟯}

@[simp]
theorem coeFn_mk (f : M → M') (hf : ContMdiff I I' n f) : (mk f hf : M → M') = f :=
  rfl
#align cont_mdiff_map.coe_fn_mk ContMdiffMap.coeFn_mk

protected theorem contMdiff (f : C^n⟮I, M; I', M'⟯) : ContMdiff I I' n f :=
  f.contMdiff_toFun
#align cont_mdiff_map.cont_mdiff ContMdiffMap.contMdiff

protected theorem smooth (f : C^∞⟮I, M; I', M'⟯) : Smooth I I' f :=
  f.contMdiff_toFun
#align cont_mdiff_map.smooth ContMdiffMap.smooth

protected theorem mdifferentiable' (f : C^n⟮I, M; I', M'⟯) (hn : 1 ≤ n) : Mdifferentiable I I' f :=
  f.ContMdiff.Mdifferentiable hn
#align cont_mdiff_map.mdifferentiable' ContMdiffMap.mdifferentiable'

protected theorem mdifferentiable (f : C^∞⟮I, M; I', M'⟯) : Mdifferentiable I I' f :=
  f.ContMdiff.Mdifferentiable le_top
#align cont_mdiff_map.mdifferentiable ContMdiffMap.mdifferentiable

protected theorem mdifferentiableAt (f : C^∞⟮I, M; I', M'⟯) {x} : MdifferentiableAt I I' f x :=
  f.Mdifferentiable x
#align cont_mdiff_map.mdifferentiable_at ContMdiffMap.mdifferentiableAt

theorem coe_inj ⦃f g : C^n⟮I, M; I', M'⟯⦄ (h : (f : M → M') = g) : f = g := by
  cases f <;> cases g <;> cases h <;> rfl
#align cont_mdiff_map.coe_inj ContMdiffMap.coe_inj

@[ext]
theorem ext (h : ∀ x, f x = g x) : f = g := by cases f <;> cases g <;> congr <;> exact funext h
#align cont_mdiff_map.ext ContMdiffMap.ext

instance : ContinuousMapClass C^n⟮I, M; I', M'⟯ M M'
    where
  coe f := ⇑f
  coe_injective' := coe_inj
  map_continuous f := f.ContMdiff.Continuous

/-- The identity as a smooth map. -/
def id : C^n⟮I, M; I, M⟯ :=
  ⟨id, contMdiff_id⟩
#align cont_mdiff_map.id ContMdiffMap.id

/-- The composition of smooth maps, as a smooth map. -/
def comp (f : C^n⟮I', M'; I'', M''⟯) (g : C^n⟮I, M; I', M'⟯) : C^n⟮I, M; I'', M''⟯
    where
  toFun a := f (g a)
  contMdiff_toFun := f.contMdiff_toFun.comp g.contMdiff_toFun
#align cont_mdiff_map.comp ContMdiffMap.comp

@[simp]
theorem comp_apply (f : C^n⟮I', M'; I'', M''⟯) (g : C^n⟮I, M; I', M'⟯) (x : M) :
    f.comp g x = f (g x) :=
  rfl
#align cont_mdiff_map.comp_apply ContMdiffMap.comp_apply

instance [Inhabited M'] : Inhabited C^n⟮I, M; I', M'⟯ :=
  ⟨⟨fun _ => default, contMdiff_const⟩⟩

/-- Constant map as a smooth map -/
def const (y : M') : C^n⟮I, M; I', M'⟯ :=
  ⟨fun x => y, contMdiff_const⟩
#align cont_mdiff_map.const ContMdiffMap.const

end ContMdiffMap

instance ContinuousLinearMap.hasCoeToContMdiffMap :
    Coe (E →L[𝕜] E') C^n⟮𝓘(𝕜, E), E; 𝓘(𝕜, E'), E'⟯ :=
  ⟨fun f => ⟨f.toFun, f.ContMdiff⟩⟩
#align continuous_linear_map.has_coe_to_cont_mdiff_map ContinuousLinearMap.hasCoeToContMdiffMap


import sheaves_of_modules.defs
/-

# Internal homs

This work is all due to Jujian Zhang.

If 𝓜 and 𝓝 are sheaves of modules on a ringed space X, then Hom(𝓜,𝓝) naturally
has the structure of a sheaf of modules on X.

-/

/-

Given (U : topological_space.opens (X : TOP))

I want
{U : Top} -- this is Top.of U
{f : U ⟶ (X : Top.{v})}
(h : open_embedding f)

lemma open_embedding_of_le {U V : opens α} (i : U ≤ V) :
  open_embedding (set.inclusion i) :=
{ inj := set.inclusion_injective i,
  induced := (@induced_compose _ _ _ _ (set.inclusion i) coe).symm,
  open_range :=
  begin
    rw set.range_inclusion i,
    exact U.property.preimage continuous_subtype_val
  end, }

-/

section examples

variables (X : RINGED_SPACE) (U : topological_space.opens (X : TOP))

#check (Top.of U : TOP)

example : Top.of U ⟶ X := _

end examples
#exit
def RINGED_SPACE.restrict (X : RINGED_SPACE) (U : topological_space.opens (X : TOP)) :
  RINGED_SPACE :=
{ carrier := _,
  presheaf := _,
  is_sheaf := _ }

namespace SHEAF_OF_MODULES

#check algebraic_geometry.SheafedSpace.restrict

def restrict (X : RINGED_SPACE) (U : topological_space.opens (X : TOP)) :
  RINGED_SPACE :=
_

variables {X : RINGED_SPACE}

def internal_hom (𝓜 𝓝 : SHEAF_OF_MODULES X) : SHEAF_OF_MODULES X :=
{ ab_sheaf :=
  { val :=
    { obj := _,
      map := _,
      map_id' := sorry,
  map_comp' := sorry },
    cond := _ },
  module_structure := _,
  compatibility_bit := _ }

end SHEAF_OF_MODULES


example : ∀ a b c : ℕ, a = b → a = c → c = b :=
begin
  intros,
  transitivity,
  symmetry,
  assumption,
  assumption
end


import semantics.syntacticPoset deduction.deduction_cartesian deduction.PPC_natDeduct deduction.MPPC_natDeduct

namespace synPoset_cartesian

open synPoset
open deduction_basic
open deduction_cart
open deduction_monadic

variable {Form : Type}

  -- ⊤ : Form_eq
  instance [Der : has_ltop Form] : has_top (Form _eq) := ⟨ ⦃Der.top⦄ ⟩

  lemma and_liftable1 [Der : has_and Form] :
    ∀ φ ψ ψ' : Form, (ψ ⊣⊢ ψ') → ⦃φ & ψ⦄ = ⦃φ & ψ'⦄ :=
  begin
    assume φ ψ ψ' h,
    apply quotient.sound,
    cases h with ψψ' ψ'ψ,
    split,
    -- Proof that φ&ψ ⊢ φ&ψ'
      apply Der.and_intro,
      apply cart_x.and_eliml1,
      apply derive_trans,
      apply cart_x.and_elimr1,
      exact ψψ',
    -- Proof that φ&ψ' ⊢ φ&ψ
      apply Der.and_intro,
      apply cart_x.and_eliml1,
      apply derive_trans,
      apply cart_x.and_elimr1,
      exact ψ'ψ,
  end

  lemma and_liftable2 [Der : has_and Form] :
    ∀ φ φ' ψ : Form, (φ ⊣⊢ φ') → ⦃φ & ψ⦄ = ⦃φ' & ψ⦄ :=
  begin
      assume φ φ' ψ h,
      apply quotient.sound,
      cases h with φφ' φ'φ,
      split,
      -- Proof that φ&ψ ⊢ φ'&ψ
        apply Der.and_intro,
        apply derive_trans φ,
        apply cart_x.and_eliml1,
        exact φφ',
        apply cart_x.and_elimr1,
      -- Proof that φ'&ψ ⊢ φ&ψ
        apply Der.and_intro,
        apply derive_trans φ',
        apply cart_x.and_eliml1,
        exact φ'φ,
        apply cart_x.and_elimr1,
  end

  def and_eq [Der : has_and Form] : Form _eq → Form _eq → Form _eq :=
    quot.lift₂ (λ φ ψ, ⦃φ & ψ⦄) (@and_liftable1 Form Der) and_liftable2

  -- ⊓ : Form_eq → Form_eq → Form_eq
  instance [Der : has_and Form] : has_inf (Form _eq) := ⟨ and_eq ⟩

  lemma impl_liftable1 [Der : has_impl Form] :
    ∀ φ ψ ψ' : Form, (ψ ⊣⊢ ψ') → ⦃φ ⊃ ψ⦄ = ⦃φ ⊃ ψ'⦄ :=
  begin
      assume φ ψ ψ' h,
      apply quotient.sound,
      cases h with ψψ' ψ'ψ,
      constructor,
      -- Proof that φ ⊃ ψ ⊢ φ ⊃ ψ'
        apply Der.impl_intro,
        apply Der.derive_Trans ψ,
        apply cart_x.and_internal,
        exact cart_x.modus_ponens,
        exact ψψ',
      -- Proof that φ ⊃ ψ' ⊢ φ ⊃ ψ
        apply Der.impl_intro,
        apply Der.derive_Trans ψ',
        apply cart_x.and_internal,
        exact cart_x.modus_ponens,
        exact ψ'ψ,
  end

  lemma impl_liftable2 [Der : has_impl Form] :
    ∀ φ φ' ψ : Form, (φ ⊣⊢ φ') → ⦃φ ⊃ ψ⦄ = ⦃φ' ⊃ ψ⦄ :=
  begin
      assume φ φ' ψ h,
      apply quotient.sound,
      cases h with φφ' φ'φ,
      constructor,
      -- Proof that φ ⊃ ψ ⊢ φ' ⊃ ψ
        apply Der.impl_intro,
        apply Der.impl_elim φ,
        apply Der.weak1,
        apply derive_refl,
        apply cart_x.insert_trans,
        exact φ'φ,
      -- Proof that φ' ⊃ ψ ⊢ φ ⊃ ψ
        apply Der.impl_intro,
        apply Der.impl_elim φ',
        apply Der.weak1,
        apply derive_refl,
        apply cart_x.insert_trans,
        exact φφ',
  end

  def impl_eq [Der : has_impl Form] : Form _eq → Form _eq → Form _eq :=
    quot.lift₂ (λ φ ψ, ⦃φ ⊃ ψ⦄) (@impl_liftable1 Form Der) impl_liftable2

  -- ⇨ : Form_eq → Form_eq → Form_eq
  instance [Der : has_impl Form] : has_himp (Form _eq) := ⟨ impl_eq ⟩

end synPoset_cartesian

/- Instances -/
namespace PPC_synPoset

  open synPoset
  open PPC_defn
  open synPoset_cartesian

  def PPC_eq := @Form_eq PPC_Form PPC_has_derives.PPC_Der

end PPC_synPoset

namespace MPPC_synPoset

  open synPoset
  open MPPC_defn
  open synPoset_cartesian

  def MPPC_eq := @Form_eq MPPC_Form MPPC_has_derives.MPPC_Der

end MPPC_synPoset


import ring_theory.localization
import ring_theory.subring
import continuous_valuations
import Huber_pair

universes u₁ u₂ u₃

local attribute [instance] classical.prop_decidable

open set function Spv

-- Wedhorn def 7.23.
definition Spa (A : Huber_pair) : set (Spv A) :=
{v | (v ∈ Cont A) ∧ ∀ r, r ∈ A⁺ → v r ≤ 1}

lemma mk_mem_Spa {A : Huber_pair} {v : Valuation A} : mk v ∈ Spa A ↔ (mk v ∈ Cont A) ∧ ∀ r, r ∈ A⁺ → v r ≤ 1 :=
begin
  split; intro h; split; try { exact h.left };
  intros r hr,
  { rw ← (v.val).map_one,
    apply (out_mk r 1).mp,
    convert h.right r hr,
    exact valuation.map_one _, },
  { rw ← (v.val).map_one at h,
    convert (out_mk r 1).mpr (h.right r hr),
    exact (valuation.map_one _).symm }
end

namespace Spa

variable {A : Huber_pair}

instance : has_coe (Spa A) (Spv A) := ⟨subtype.val⟩

definition basic_open (r s : A) : set (Spa A) :=
{v | v r ≤ v s ∧ v s ≠ 0 }

lemma mk_mem_basic_open {r s : A} {v : Valuation A} {hv : mk v ∈ Spa A} :
(⟨mk v, hv⟩ : Spa A) ∈ basic_open r s ↔ v r ≤ v s ∧ v s ≠ 0 :=
begin
  split; intro h; split,
  { exact (out_mk r s).mp h.left },
  { exact Valuation.ne_zero_of_equiv_ne_zero out_mk h.right },
  { exact (out_mk r s).mpr h.left },
  { exact Valuation.ne_zero_of_equiv_ne_zero (setoid.symm out_mk) h.right }
end

instance (A : Huber_pair) : topological_space (Spa A) :=
topological_space.generate_from {U : set (Spa A) | ∃ r s : A, U = basic_open r s}

lemma basic_open.is_open (r s : A) : is_open (basic_open r s) :=
topological_space.generate_open.basic (basic_open r s) ⟨r, ⟨s, rfl⟩⟩

lemma basic_open_eq (s : A) : basic_open s s = {v | v s ≠ 0} :=
set.ext $ λ v, ⟨λ h, h.right, λ h, ⟨le_refl _, h⟩⟩

-- should only be applied with (HfinT : fintype T) and (Hopen: is_open (span T))
definition rational_open (s : A) (T : set A) : set (Spa A) :=
{v | (∀ t ∈ T, (v t ≤ v s)) ∧ (v s ≠ 0)}

lemma mk_mem_rational_open {s : A} {T : set A} {v : Valuation A} {hv : mk v ∈ Spa A} :
(⟨mk v, hv⟩ : Spa A) ∈ rational_open s T ↔ (∀ t ∈ T, (v t ≤ v s)) ∧ (v s ≠ 0) :=
begin
  split; intro h; split,
  { intros t ht,
    exact (out_mk t s).mp (h.left t ht) },
  { exact Valuation.ne_zero_of_equiv_ne_zero out_mk h.right },
  { intros t ht,
    exact (out_mk t s).mpr (h.left t ht) },
  { exact Valuation.ne_zero_of_equiv_ne_zero (setoid.symm out_mk) h.right }
end

definition rational_open_Inter (s : A) (T : set A) :
rational_open s T = (set.Inter (λ (t : T), basic_open t s)) ∩ {v | v s ≠ 0} :=
set.ext $ λ v, ⟨λ ⟨H1, H2⟩, ⟨set.mem_Inter.2 $ λ t, ⟨H1 _ t.property, H2⟩, H2⟩,
  λ ⟨H1, H2⟩, ⟨λ t ht, (set.mem_Inter.1 H1 ⟨t, ht⟩).1, H2⟩⟩

lemma rational_open_add_s (s : A) (T : set A) :
rational_open s T = rational_open s (insert s T) :=
set.ext $ λ v,
⟨ λ ⟨H1, H2⟩, ⟨λ t Ht, or.rec_on Ht (λ H, by rw H; exact le_refl _) (H1 t), H2⟩,
  λ ⟨H1, H2⟩, ⟨λ t Ht, H1 t $ set.mem_insert_of_mem _ Ht,H2⟩⟩

lemma rational_open.is_open (s : A) (T : set A) [fintype T] :
is_open (rational_open s T) :=
begin
  rw rational_open_Inter,
  apply is_open_inter, swap, rw ← basic_open_eq s, exact basic_open.is_open s s,
  simpa using @is_open_bInter _ _ _ _ (λ t : T, basic_open t.1 s)
    (finite_mem_finset finset.univ) (λ t ht, basic_open.is_open t s),
end

lemma rational_open_inter.aux1 {s₁ s₂ : A} {T₁ T₂ : set A} [fintype T₁] [fintype T₂] (h₁ : s₁ ∈ T₁) (h₂ : s₂ ∈ T₂) :
rational_open s₁ T₁ ∩ rational_open s₂ T₂ ⊆ rational_open (s₁ * s₂) ((*) <$> T₁ <*> T₂) :=
begin
  rintros v ⟨⟨hv₁, hs₁⟩, ⟨hv₂, hs₂⟩⟩,
  have vmuls : v (s₁ * s₂) = v s₁ * v s₂ := valuation.map_mul _ _ _,
  split,
  { rintros t ⟨_, ⟨t₁, ht₁, rfl⟩, t₂, ht₂, ht⟩,
    subst ht,
    have vmult : v (t₁ * t₂) = v t₁ * v t₂ := valuation.map_mul _ _ _,
    rw [vmuls, vmult],
    refine le_trans (linear_ordered_comm_monoid.mul_le_mul_left  (hv₂ _ ht₂) _)
                    (linear_ordered_comm_monoid.mul_le_mul_right (hv₁ _ ht₁) _ ) },
  { intro H,
    rw vmuls at H,
    cases H1 : v s₁ with γ₁, exact hs₁ H1,
    cases H2 : v s₂ with γ₂, exact hs₂ H2,
    rw [H1,H2] at H,
    change some (γ₁ * γ₂) = none at H,
    exact option.no_confusion H },
end

lemma rational_open_inter.aux2 {s₁ s₂ : A} {T₁ T₂ : set A} [fintype T₁] [fintype T₂] (h₁ : s₁ ∈ T₁) (h₂ : s₂ ∈ T₂) :
rational_open (s₁ * s₂) ((*) <$> T₁ <*> T₂) ⊆ rational_open s₁ T₁ ∩ rational_open s₂ T₂ :=
begin
  rintros v ⟨hv,hs⟩,
  have vmuls : v (s₁ * s₂) = v s₁ * v s₂ := valuation.map_mul _ _ _,
  have vs₁ne0 : v s₁ ≠ 0 := λ H, by simpa only [vmuls,H,zero_mul,ne.def,eq_self_iff_true,not_true] using hs,
  have vs₂ne0 : v s₂ ≠ 0 := λ H, by simpa only [vmuls,H,mul_zero,ne.def,eq_self_iff_true,not_true] using hs,
  split; split,
  { intros t ht,
    suffices H : v t * v s₂ ≤ v s₁ * v s₂,
    { cases H' : v s₂ with γ, exfalso; exact vs₂ne0 H',
      rw H' at H,
      have := linear_ordered_comm_monoid.mul_le_mul_right H (some (γ⁻¹)),
      conv at this { to_lhs, rw mul_assoc, congr, skip, change some (γ * γ⁻¹) },
      conv at this { to_rhs, rw mul_assoc, congr, skip, change some (γ * γ⁻¹) },
      simp only [mul_right_inv] at this,
      change v t * 1 ≤ v s₁ * 1 at this,
      rwa [mul_one,mul_one] at this },
    { rw ←vmuls,
      rw show v t * v s₂ = v (t * s₂), from (valuation.map_mul _ _ _).symm,
      refine hv (t * s₂) ⟨_,⟨_,ht,rfl⟩,_,h₂,rfl⟩ } },
  { exact vs₁ne0 },
  { intros t ht,
    suffices H : v s₁ * v t ≤ v s₁ * v s₂,
    { cases H' : v s₁ with γ, exfalso; exact vs₁ne0 H',
      rw H' at H,
      have := linear_ordered_comm_monoid.mul_le_mul_left H (some (γ⁻¹)),
      conv at this { to_lhs, rw ← mul_assoc, congr, change some (γ⁻¹ * γ) },
      conv at this { to_rhs, rw ← mul_assoc, congr, change some (γ⁻¹ * γ) },
      simp only [mul_left_inv] at this,
      change 1 * v t ≤ 1 * v s₂ at this,
      rwa [one_mul,one_mul] at this },
    { rw ←vmuls,
      rw show v s₁ * v t = v (s₁ * t), from (valuation.map_mul _ _ _).symm,
      refine hv _ ⟨_, ⟨s₁, h₁, rfl⟩, t, ht, rfl⟩ } },
  { exact vs₂ne0 }
end

lemma rational_open_inter {s₁ s₂ : A} {T₁ T₂ : set A} [fintype T₁] [fintype T₂] (h₁ : s₁ ∈ T₁) (h₂ : s₂ ∈ T₂) :
rational_open s₁ T₁ ∩ rational_open s₂ T₂ = rational_open (s₁ * s₂) ((*) <$> T₁ <*> T₂) :=
begin
  ext v, split; intro h,
  exact rational_open_inter.aux1 h₁ h₂ h,
  exact rational_open_inter.aux2 h₁ h₂ h
end

@[simp] lemma rational_open_singleton {r s : A} :
rational_open s {r} = basic_open r s :=
ext $ λ v,
{ mp  := λ h, ⟨h.left r (mem_singleton_iff.mpr rfl), h.right⟩,
  mpr := λ h, ⟨λ t ht,
          begin
            rw mem_singleton_iff at ht, subst ht,
            exact h.left
          end, h.right⟩ }

@[simp] lemma basic_open_eq_univ : basic_open (1 : A) (1 : A) = univ :=
begin
  apply le_antisymm,
  { exact subset_univ _ },
  { intros v h,
    split,
    exact le_refl _,
    have v1 : v 1 = 1 := valuation.map_one _,
    rw v1,
    intro h, exact option.no_confusion h },
end

@[simp] lemma rational_open_eq_univ : rational_open (1 : A) {(1 : A)} = univ :=
by simp

def rational_basis (A : Huber_pair) : set (set (Spa A)) :=
{U : set (Spa A) | ∃ {s : A} {T : set A} {h : fintype T}, U = rational_open s T }

attribute [instance] set.fintype_seq -- should move to mathlib

lemma rational_basis.is_basis : topological_space.is_topological_basis (rational_basis A) :=
begin
split,
{ rintros U₁ ⟨s₁, T₁, hfin₁, H₁⟩ U₂ ⟨s₂, T₂, hfin₂, H₂⟩ v hv,
  haveI := hfin₁,
  haveI := hfin₂,
  existsi U₁ ∩ U₂,
  rw rational_open_add_s at H₁ H₂,
  split,
  { simp [H₁, H₂,rational_open_inter,-set.fmap_eq_image,-set.seq_eq_set_seq],
    exact ⟨_,_,by apply_instance,rfl⟩ },
  { exact ⟨hv, subset.refl _⟩ } },
split,
{ apply le_antisymm,
  { exact subset_univ _ },
  apply subset_sUnion_of_mem,
  refine ⟨(1 : A), {(1 : A)}, by apply_instance, by simp⟩ },
{ apply le_antisymm,
  { unfold Spa.topological_space,
    rw generate_from_le_iff_subset_is_open,
    rintros U ⟨r, s, H⟩,
    rw [H,←rational_open_singleton],
    refine topological_space.generate_open.basic _ ⟨s, {r}, _, rfl⟩,
    exact set.fintype_singleton _ },
  { rw generate_from_le_iff_subset_is_open,
    rintros U ⟨s, T, hT, H⟩,
    subst H,
    haveI := hT,
    exact rational_open.is_open s T,
  } }
end

namespace rational_open
def presheaf.ring (s : A) := localization.away s

instance (s : A) : comm_ring (presheaf.ring s) :=
by dunfold presheaf.ring ; apply_instance

def localize (s : A) : A → presheaf.ring s := localization.of_comm_ring A _

-- Definition of A\left(\frac T s\right) as a topological ring
def presheaf.top_ring (s : A) (T : set A) (HfinT : fintype T) :
   topological_space (presheaf.ring s) :=
 let As := presheaf.ring s in sorry
 /-let D := ring.closure ((localize s) '' A.RHuber.A₀ ∪ (((λ x, x*s⁻¹) ∘ localize s) '' T)) in
 begin
   let nhd := λ n : ℕ, mul_ideal (pow_ideal ((localize s) '' A.Rplus) n) D,
  sorry
end-/
end rational_open
end Spa

-- goal now to define the 𝓞_X on *rational subsets* and then to extend.

-- to define it on rational subsets it's just a ring completion.

-- remember that a rational open is not actually `rational_open s T` in full
-- generality -- we also need that T is finite and that T generates an open ideal in A.
-- The construction on p73/74 (note typo in first line of p74 -- ideal should be I.D)
-- gives A<T/s> (need completion) and A<T/s>^+ (need integral closure).

-- Once we have this, we see mid way through p75 that the definition of the presheaf
-- on V is proj lim of O_X(U) as U runs through rationals opens in V. This gets
-- the projective limit topology and then we have a presheaf (hopefully this is
-- straightforward) of complete topological rings (need proj lim of complete is complete)

-- We then need the valuations on the stalks (this is direct limit in cat of rings, forget
-- the topology). This will be fiddly but not impossible.

-- We then have an object in V^pre and I think then everything should follow.


/-
Copyright (c) 2020 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.nat.basic
import Mathlib.PostPort

namespace Mathlib

/-!
# Natural number logarithm

This file defines `log b n`, the logarithm of `n` with base `b`, to be the largest `k` such that
`b ^ k ≤ n`.

-/

namespace nat

/-- `log b n`, is the logarithm of natural number `n` in base `b`. It returns the largest `k : ℕ`
such that `b^k ≤ n`, so if `b^k = n`, it returns exactly `k`. -/
def log (b : ℕ) : ℕ → ℕ :=
  sorry

theorem pow_le_iff_le_log (x : ℕ) (y : ℕ) {b : ℕ} (hb : 1 < b) (hy : 1 ≤ y) : b ^ x ≤ y ↔ x ≤ log b y := sorry

theorem log_pow (b : ℕ) (x : ℕ) (hb : 1 < b) : log b (b ^ x) = x := sorry

theorem pow_succ_log_gt_self (b : ℕ) (x : ℕ) (hb : 1 < b) (hy : 1 ≤ x) : x < b ^ Nat.succ (log b x) := sorry

theorem pow_log_le_self (b : ℕ) (x : ℕ) (hb : 1 < b) (hx : 1 ≤ x) : b ^ log b x ≤ x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b ^ log b x ≤ x)) (propext (pow_le_iff_le_log (log b x) x hb hx)))) (le_refl (log b x))


axiom Int.add_comm (i j : Int) : i + j = j + i

example (n : Nat) (i : Int) : n + i = i + n := by
  rw [Int.add_comm]

def f1 (a : Int) (b c : Nat) : Int :=
  a + (b - c)

def f2 (a : Int) (b c : Nat) : Int :=
  (b - c) + a

#print f1
#print f2


import topology.basic
import algebra.module.basic
import algebra.module.submodule

import analysis.normed_space.basic

-- Montrer que si un sous-espace vectoriel F d'un espace normé E est ouvert alors F = E .

theorem exo {R E: Type*} [normed_field R] [normed_group E] [normed_space R E] (F: subspace R E):
  is_open F.carrier -> forall x: E, set.mem x F
:= sorry


import analysis.normed.group.hom
import ring_theory.finiteness
import linear_algebra.free_module.basic
import ring_theory.int.basic

/-!

# Polyhedral lattices

A polyhedral lattice is a finite free ℤ-module with a real-valued norm
making it into a normed group, such that the closed unit ball of
the Banach space obtained by tensoring everything up to ℝ is a rational polyhedron.

## Implementation issues

The condition on the norm actually used is `generates_norm` below.

-/
noncomputable theory
open_locale big_operators classical nnreal

section generates_norm

variables {Λ ι : Type*} [semi_normed_group Λ] [fintype ι]

/-- A finite family `x : ι → Λ` generates the norm on `Λ`
if for every `l : Λ` there exist coefficients `c : ι → ℕ`
such that `l = ∑ i, c i • x i` and `∥l∥ = ∑ i, (c i) * ∥x i∥`.
-/
def generates_norm (x : ι → Λ) :=
∀ l : Λ, ∃ (c : ι → ℕ), (l = ∑ i, c i • x i) ∧ (∥l∥ = ∑ i, c i * ∥x i∥)

lemma generates_norm_iff_generates_nnnorm (x : ι → Λ) :
  generates_norm x ↔
  ∀ l : Λ, ∃ (c : ι → ℕ),
    (l = ∑ i, c i • x i) ∧ (∥l∥₊ = ∑ i, c i * ∥x i∥₊) :=
begin
  apply forall_congr, intro l,
  simp only [← nnreal.eq_iff, nnreal.coe_mul, nnreal.coe_sum, nnreal.coe_nat_cast, coe_nnnorm]
end

lemma generates_norm.generates_nnnorm {x : ι → Λ} (hl : generates_norm x) :
  ∀ l : Λ, ∃ (c : ι → ℕ), (l = ∑ i, c i • x i) ∧ (∥l∥₊ = ∑ i, c i * ∥x i∥₊) :=
(generates_norm_iff_generates_nnnorm x).mp hl

lemma generates_norm_of_generates_nnnorm {x : ι → Λ}
  (H : ∀ l : Λ, ∃ (c : ι → ℕ), (l = ∑ i, c i • x i) ∧ (∥l∥₊ = ∑ i, c i * ∥x i∥₊)) :
  generates_norm x :=
(generates_norm_iff_generates_nnnorm x).mpr H

end generates_norm

class polyhedral_lattice (Λ : Type*) extends normed_group Λ :=
[finite [] : module.finite ℤ Λ]
[free   [] : module.free ℤ Λ]
(polyhedral' [] : ∃ (ι : Type) [fintype ι] (l : ι → Λ), generates_norm l)

namespace polyhedral_lattice

variables (Λ : Type*) [polyhedral_lattice Λ]

attribute [instance] polyhedral_lattice.finite polyhedral_lattice.free

instance no_zero_smul_divisors_int : no_zero_smul_divisors ℤ Λ :=
module.free.no_zero_smul_divisors ℤ Λ

instance no_zero_smul_divisors_nat : no_zero_smul_divisors ℕ Λ :=
⟨λ n l h, by { rw  [← coe_nat_zsmul, smul_eq_zero] at h,
  refine h.imp _ id, simp only [imp_self, int.coe_nat_eq_zero] }⟩

lemma polyhedral :
  ∃ (ι : Type) (_ : fintype ι) (l : ι → Λ), by exactI generates_norm l ∧ ∀ i, l i ≠ 0 :=
begin
  obtain ⟨ι, _ι_inst, l, hl⟩ := polyhedral_lattice.polyhedral' Λ, resetI,
  refine ⟨{i // l i ≠ 0}, infer_instance, λ i, l i, _, λ i, i.2⟩,
  intro x,
  obtain ⟨c, h1, h2⟩ := hl x,
  refine ⟨λ i, c i, _, _⟩,
  { rw h1,
    refine finset.sum_bij_ne_zero _ (λ _ _ _, finset.mem_univ _) _ _ _,
    { rintro i - hi, refine ⟨i, _⟩, contrapose! hi, rw [hi, smul_zero] },
    { dsimp, rintro i j - hi - hj, simp only [imp_self], },
    { rintro ⟨i, hi⟩ -, dsimp, intro H, refine ⟨i, finset.mem_univ _, H, rfl⟩ },
    { intros, refl } },
  { rw h2,
    refine finset.sum_bij_ne_zero _ (λ _ _ _, finset.mem_univ _) _ _ _,
    { rintro i - hi, refine ⟨i, _⟩, contrapose! hi, rw [hi, norm_zero, mul_zero] },
    { dsimp, rintro i j - hi - hj, simp only [imp_self], },
    { rintro ⟨i, hi⟩ -, dsimp, intro H, refine ⟨i, finset.mem_univ _, H, rfl⟩ },
    { intros, refl } },
end

end polyhedral_lattice

/-- A morphism of polyhedral lattices is a norm-nonincreasing group homomorphism. -/
structure polyhedral_lattice_hom (Λ₁ Λ₂ : Type*) [polyhedral_lattice Λ₁] [polyhedral_lattice Λ₂] :=
(to_fun : Λ₁ → Λ₂)
(map_add' : ∀ l₁ l₂, to_fun (l₁ + l₂) = to_fun l₁ + to_fun l₂)
(strict' : ∀ l, ∥to_fun l∥ ≤ ∥l∥)

namespace add_monoid_hom

variables {Λ₁ Λ₂ : Type*} [polyhedral_lattice Λ₁] [polyhedral_lattice Λ₂]
variables {f g : polyhedral_lattice_hom Λ₁ Λ₂}

/-- Associate to a group homomorphism a bounded group homomorphism under a norm control condition.

See `add_monoid_hom.mk_polyhedral_lattice_hom'` for a version that uses `ℝ≥0` for the bound. -/
def mk_polyhedral_lattice_hom (f : Λ₁ →+ Λ₂) (h : ∀ v, ∥f v∥ ≤ ∥v∥) :
  polyhedral_lattice_hom Λ₁ Λ₂ :=
{ strict' := h, ..f }

/-- Associate to a group homomorphism a bounded group homomorphism under a norm control condition.

See `add_monoid_hom.mk_polyhedral_lattice_hom` for a version that uses `ℝ` for the bound. -/
def mk_polyhedral_lattice_hom' (f : Λ₁ →+ Λ₂) (h : ∀ x, ∥f x∥₊ ≤ ∥x∥₊) :
  polyhedral_lattice_hom Λ₁ Λ₂ :=
{ strict' := h, .. f}

end add_monoid_hom

namespace polyhedral_lattice_hom

variables {Λ Λ₁ Λ₂ Λ₃ : Type*}
variables [polyhedral_lattice Λ] [polyhedral_lattice Λ₁] [polyhedral_lattice Λ₂] [polyhedral_lattice Λ₃]
variables {f g : polyhedral_lattice_hom Λ₁ Λ₂}

instance : has_coe_to_fun (polyhedral_lattice_hom Λ₁ Λ₂) (λ _, Λ₁ → Λ₂):= ⟨polyhedral_lattice_hom.to_fun⟩

initialize_simps_projections polyhedral_lattice_hom (to_fun → apply)

lemma coe_inj (H : (⇑f: Λ₁ → Λ₂) = g) : f = g :=
by cases f; cases g; congr'; exact funext H

lemma coe_injective : @function.injective (polyhedral_lattice_hom Λ₁ Λ₂) (Λ₁ → Λ₂) coe_fn :=
by apply coe_inj

lemma coe_inj_iff : f = g ↔ (⇑f: Λ₁ → Λ₂) = g := ⟨congr_arg _, coe_inj⟩

@[ext] lemma ext (H : ∀ x, f x = g x) : f = g := coe_inj $ funext H

lemma ext_iff : f = g ↔ ∀ x, f x = g x := ⟨by rintro rfl x; refl, ext⟩

variables (f g)

@[simp] lemma to_fun_eq_coe : f.to_fun = f := rfl

@[simp] lemma coe_mk (f) (h₁) (h₂) : ⇑(⟨f, h₁, h₂⟩ : polyhedral_lattice_hom Λ₁ Λ₂) = f := rfl

@[simp] lemma coe_mk_polyhedral_lattice_hom (f : Λ₁ →+ Λ₂) (h) :
  ⇑(f.mk_polyhedral_lattice_hom h) = f := rfl

@[simp] lemma coe_mk_polyhedral_lattice_hom' (f : Λ₁ →+ Λ₂) (h) :
  ⇑(f.mk_polyhedral_lattice_hom' h) = f := rfl

/-- The group homomorphism underlying a bounded group homomorphism. -/
def to_add_monoid_hom (f : polyhedral_lattice_hom Λ₁ Λ₂) : Λ₁ →+ Λ₂ :=
add_monoid_hom.mk' f f.map_add'

@[simp] lemma coe_to_add_monoid_hom : ⇑f.to_add_monoid_hom = f := rfl

lemma to_add_monoid_hom_injective :
  function.injective (@polyhedral_lattice_hom.to_add_monoid_hom Λ₁ Λ₂ _ _) :=
λ f g h, coe_inj $ show ⇑f.to_add_monoid_hom = g, by { rw h, refl }

@[simp] lemma mk_to_add_monoid_hom (f) (h₁) (h₂) :
  (⟨f, h₁, h₂⟩ : polyhedral_lattice_hom Λ₁ Λ₂).to_add_monoid_hom = add_monoid_hom.mk' f h₁ := rfl

@[simp] lemma map_zero : f 0 = 0 := f.to_add_monoid_hom.map_zero

@[simp] lemma map_add (x y) : f (x + y) = f x + f y := f.to_add_monoid_hom.map_add _ _

@[simp] lemma map_sum {ι : Type*} (Λ : ι → Λ₁) (s : finset ι) :
  f (∑ i in s, Λ i) = ∑ i in s, f (Λ i) :=
f.to_add_monoid_hom.map_sum _ _

@[simp] lemma map_sub (x y) : f (x - y) = f x - f y := f.to_add_monoid_hom.map_sub _ _

@[simp] lemma map_neg (x) : f (-x) = -(f x) := f.to_add_monoid_hom.map_neg _

@[simp] lemma map_zsmul (n : ℤ) (x) : f (n • x) = n • (f x) := f.to_add_monoid_hom.map_zsmul _ _

instance : has_zero (polyhedral_lattice_hom Λ₁ Λ₂) :=
⟨(0 : Λ₁ →+ Λ₂).mk_polyhedral_lattice_hom (by simp [le_refl])⟩

lemma strict (l : Λ₁) : ∥f l∥ ≤ ∥l∥ := f.strict' l

lemma strict_nnnorm (l : Λ₁) : ∥f l∥₊ ≤ ∥l∥₊ := f.strict' l

@[simps]
def to_normed_group_hom : normed_group_hom Λ₁ Λ₂ :=
{ bound' := ⟨1, by simpa only [one_mul] using f.strict⟩, .. f }

/-- The identity as a polyhedral lattice hom. -/
@[simps]
def id : polyhedral_lattice_hom Λ Λ :=
(add_monoid_hom.id Λ).mk_polyhedral_lattice_hom (by simp [le_refl])

/-- The composition of polyhedral lattice homs. -/
@[simps]
protected def comp (g : polyhedral_lattice_hom Λ₂ Λ₃) (f : polyhedral_lattice_hom Λ₁ Λ₂) :
  polyhedral_lattice_hom Λ₁ Λ₃ :=
(g.to_add_monoid_hom.comp f.to_add_monoid_hom).mk_polyhedral_lattice_hom $
  λ l, (g.strict' _).trans (f.strict' _)

end polyhedral_lattice_hom


inductive P : Option Nat → Prop
  | none : P .none
  | somePos : x > 0 → P (some x)

def Option.get : (a : Option α) → a.isSome → α
  | some a, _ => a

theorem aux (x? : Option Nat) (h₁ : P x?) (h₂ : x?.isSome) : x?.get h₂ > 0 := by
  cases h₁ with
  | none => contradiction
  | somePos h => exact h

def f (x? : Option Nat) (hp : P x?) : { r? : Option Nat // P r? } :=
  if h₁ : x?.isSome then
    let x := x?.get h₁
    have : x > 0 := by
      cases h₂ : x with
      | zero => have := aux x? hp h₁; simp at h₂; simp [h₂] at this
      | succ x' => simp_arith
    ⟨some x, .somePos this⟩
  else
    ⟨none, .none⟩

def f₁ (x : Nat) : Nat :=
  let y := x + 1
  by cases y with
     | zero => exact 2
     | succ y' => exact 1

example : f₁ x = 1 := rfl

noncomputable def f₂ (x : Nat) : Nat :=
  let y := x + 1
  by induction y with
     | zero => exact 2
     | succ y' => exact 1

example : f₂ x = 1 := rfl


/- This definition of the "term" datatype is mutually inductive, along with a "term_vec" datatype.
  It would be more elegant and probably easier to use one inductive type Lean library's "vector" datatype,
  but it doesn't work out of the box due to (vector α n) taking the type α as an argument:

        inductive term' : Type
            | var : ℕ → Type
            | apply : ∀ f : L.functions, vector term' (L.arityF f) → term'

  If we use this definition, there is an error message saying the nested occurrence of the type "contains variables that are not parameters."

  The implementation here also has problems: Lean cannot figure out how to prove that induction over the  mutually inductive types term/term_vec is well-founded.
  One of Lean's basic strategies to accomplish this is the has_sizeof typeclass; has_sizeof.sizeof is, ideally, a function from a Type to ℕ which decreases w.r.t. complexity;
  For example sizeof (term.apply f t_vec) should be greater than sizeof t_vec; and in turn sizeof t_vec should be greater than that of its members.
  The two functions sizeof_vec and sizeof_term_vec are meant to accomplish this, but it hasn't been done quite right;
  I think the problem might have to do with the use of "sizeof" within the two functions: because the  function sizeof_term is passed an instance of has_sizeof  term,
  this means that the and means that the outermost "sizeof term" is slightly, and invisibly, different than the innermost "sizeof term". Typeclasses are a mystery to me.
  Whatever the problem, it leads to this cryptic error , which you can see below on the first use of free_vars_vec:

    nested exception message:
        failed
        state:
        L : Language,
        f : L.functions,
        l : term_vec L (L.arityF f),
        this : 1 + (L.arityF f + sizeof_term_vec L (L.arityF f) l) < sizeof_term L (term.apply f l)
        ⊢ 1 + (L.arityF f + sizeof_term_vec L (L.arityF f) l) < sizeof_term L (term.apply f l)

    There must be a slight difference between the type of "this" and the conclusion Lean wishes to reach, or it could be discharged immediately.
 -/
import data.vector

structure Language :=
    language :: (relations : Type) (functions : Type ) (arityF :  functions →  nat) (arityR : relations → nat)
variable L : Language

universe u
mutual inductive  term, term_vec
with term : Type
    | var : nat → term
    | apply: ∀ (f : L.functions ), (term_vec (L.arityF f)) → term
with term_vec : Π (n : nat), Type
    | nil : term_vec 0
    | cons : Π {n: nat}, term → term_vec n → term_vec (n+1)

def term_vec.length {n : nat} : term_vec L n → ℕ := λ t, n

def in_term_vec : Π n, term L → term_vec L n → Prop
    | 0 t (term_vec.nil L) := false
    | (m + 1) t (term_vec.cons t' ts) := t = t' ∨ in_term_vec m t ts

def rel_term : term L → term L → Prop
    | (term.var _ n) (term.var _ m) := false
    |  t (term.apply f ts) := (in_term_vec L (term_vec.length L ts) t ts ) ∨  (∃ t', (in_term_vec L (term_vec.length L ts) t' ts ∧ rel_term t t'))

 def  sizeof_term_vec [β : has_sizeof (term L)] : Π n, term_vec L n  → nat
    | 0 (term_vec.nil L) := 0
    | _ (term_vec.cons (term.var _ _ ) ts) := 1 + sizeof ts
    | _ (term_vec.cons (term.apply f ts') ts) := sizeof ts' + sizeof ts

instance term_vec_has_sizeof {n : nat} : has_sizeof (term_vec L n) := ⟨ sizeof_term_vec L n ⟩

def sizeof_term [β : has_sizeof (Π n, (term_vec L n))] : term L → ℕ
    | (term.var _ _ ) := 0
    | (term.apply f ts) := 2 + (L.arityF f) + sizeof ts

instance term_has_sizeof : has_sizeof (term L) := ⟨ sizeof_term L ⟩

mutual def free_vars, free_vars_vec
with free_vars : (term L) → list nat
    | (term.var L n) := [n]
    | (term.apply f l) :=  have 1 + (L.arityF f + sizeof_term_vec L (L.arityF f) l) < sizeof_term L (term.apply f l), by
    {simp [sizeof_term, sizeof, has_sizeof.sizeof, add_lt_add_left ], admit},
    free_vars_vec (L.arityF f) l
with free_vars_vec : Π n, (term_vec L n) → list nat
    | 0 (term_vec.nil L) := []
    | _ (term_vec.cons t ts) := nat_list_union (free_vars t) (free_vars_vec ts)

definition substitute_formula : list nat → list (term L) → formula L := sorry


/-
Copyright (c) 2022 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: María Inés de Frutos-Fernández
-/
import basic
import mul_ring_norm_rat
import nonarchimedean
import function_field
--import number_theory.padics.padic_norm
--import order.filter.basic
--import analysis.special_functions.log.base
--import analysis.normed.ring.seminorm
--import data.nat.digits
--import ring_theory.power_series.basic
--import analysis.specific_limits.normed
--import topology.algebra.order.basic

open_locale big_operators

/-!
# Ostrowski's theorem for ℚ

## References
* https://kconrad.math.uconn.edu/blurbs/gradnumthy/ostrowskinumbfield.pdf

## Tags
ring_norm, ostrowski
-/

noncomputable theory

section real

/-- The usual absolute value on ℚ. -/
def mul_ring_norm.real : mul_ring_norm ℚ :=
{ to_fun    := λ x : ℚ, |x|,
  map_zero' := by simp only [rat.cast_zero, abs_zero],
  add_le'   := norm_add_le,
  neg'      := norm_neg,
  eq_zero_of_map_eq_zero' :=
  by simp only [abs_eq_zero, rat.cast_eq_zero, imp_self, forall_const],
  map_one' := by simp only [algebra_map.coe_one, abs_one],
  map_mul' := by exact_mod_cast abs_mul,
}

@[simp] lemma mul_ring_norm_eq_abs (r : ℚ) : mul_ring_norm.real r = |r| := rfl

end real

section padic

/-- The p-adic norm on ℚ. -/
def mul_ring_norm.padic (p : ℕ) [hp : fact (nat.prime p)] : mul_ring_norm ℚ :=
{ to_fun    := λ x : ℚ, (padic_norm p x: ℝ),
  map_zero' := by simp only [padic_norm.zero, rat.cast_zero],
  add_le'   := by norm_cast; exact padic_norm.triangle_ineq,
  neg'      := by simp only [padic_norm.neg, eq_self_iff_true, forall_const],
  eq_zero_of_map_eq_zero' := by norm_cast; exact @padic_norm.zero_of_padic_norm_eq_zero p _,
  map_one' := by exact_mod_cast padic_norm.one,
  map_mul' := by simp only [padic_norm.mul, rat.cast_mul, eq_self_iff_true, forall_const],
}

@[simp] lemma mul_ring_norm_eq_padic_norm (p : ℕ) [hp : fact (nat.prime p)] (r : ℚ) :
  mul_ring_norm.padic p r = padic_norm p r := rfl

lemma mul_ring_norm.padic_is_nonarchimedean (p : ℕ) [hp : fact (nat.prime p)] :
  is_nonarchimedean (@mul_ring_norm.padic p hp) :=
begin
  simp only [is_nonarchimedean_def, mul_ring_norm_eq_padic_norm],
  exact_mod_cast @padic_norm.nonarchimedean p _
end

end padic

variable {f : mul_ring_norm ℚ}

section non_archimedean

-- Show that 𝔞 is an ideal
-- Maybe this should be inserted into the final proof.
def 𝔞 (harc : is_nonarchimedean f) : ideal ℤ :=
{ carrier := {a : ℤ | f a < 1},
  add_mem' := λ a b ha hb, by simp only [set.mem_set_of_eq, int.cast_add] at ha hb ⊢;
    linarith [(harc a b), (max_lt ha hb)],
  zero_mem' := by simp only [set.mem_set_of_eq, algebra_map.coe_zero, map_zero, zero_lt_one],
  smul_mem' := λ a b hb, by simp only [algebra.id.smul_eq_mul, set.mem_set_of_eq, int.cast_mul,
    map_mul, mul_lt_of_le_of_lt_one' (int_norm_le_one a harc) hb (map_nonneg f b) zero_lt_one]}

--Maybe this should be inserted into the final proof.
lemma a_proper (harc : is_nonarchimedean f) : 𝔞 harc ≠ (⊤ : ideal ℤ) :=
begin
  rw ideal.ne_top_iff_one,
  dsimp only [𝔞, submodule.mem_mk, set.mem_set_of_eq, int.cast_one, not_lt],
  simp only [algebra_map.coe_one, map_one, lt_self_iff_false, not_false_iff],
end

-- Show that it contains pZ
-- Maybe this should be inserted into the final proof.
lemma a_contains_prime_ideal (harc : is_nonarchimedean f) (h_nontriv : f ≠ 1) :
  ∃ (p : ℕ) [hp : fact (nat.prime p)], 𝔞 harc ≥ ideal.span {p} :=
begin
  obtain ⟨p, hp, hbound⟩ := ex_prime_norm_lt_one harc h_nontriv,
  refine ⟨p, hp, _⟩,
  { apply ideal.span_le.mpr,
    simp only [set.singleton_subset_iff, set_like.mem_coe],
    exact hbound, }
end

-- Show that it's in fact equal to pZ (since pZ is a maximal ideal)
-- Maybe this should be inserted into the final proof.
lemma a_eq_prime_ideal (harc : is_nonarchimedean f) (h_nontriv : f ≠ 1) :
  ∃ (p : ℕ) [hp : fact (nat.prime p)], 𝔞 harc = ideal.span {p} :=
by obtain ⟨p, hp, hinc⟩ := a_contains_prime_ideal harc h_nontriv;
  refine ⟨p, hp, ((principal_ideal_ring.is_maximal_of_irreducible
    (nat.prime_iff_prime_int.mp hp.1).irreducible).eq_of_le (a_proper harc) hinc).symm⟩

-- I will try to see whether there is a similar version of this (hopefully)
lemma mult_finite {a : ℤ} {p : ℕ} (hp : nat.prime p) (ha : a ≠ 0) :
  multiplicity.finite (p : ℤ) a :=
by apply multiplicity.finite_int_iff.mpr;
  simp only [ha, hp.ne_one, int.nat_abs_of_nat, ne.def, not_false_iff, and_self]

-- the equality at the end of the next lemma
lemma rearrange {p v : ℝ} (m : ℕ) (hp : p > 0) (hlogp : real.log p ≠ 0) (hv : v > 0) :
  v ^ m = (p ^ m)⁻¹ ^ (-real.log v / real.log p) :=
begin
  have : p ^ m = p ^ (m : ℝ) := by norm_cast,
  rw [←real.rpow_neg_one, this, ←(real.rpow_mul (le_of_lt hp)),
    ←(real.rpow_mul (le_of_lt hp)), neg_div, mul_neg, mul_neg, mul_one, neg_mul, neg_neg,
      mul_div, ←real.log_rpow hv, real.rpow_def_of_pos hp, mul_div_left_comm,
        div_self hlogp, mul_one, real.exp_log],
  { norm_cast },
  { norm_cast,
    exact pow_pos hv m }
end

-- f a = (f p)^m = ring_norm a
lemma int_val_eq (harc : is_nonarchimedean f) (h_nontriv : f ≠ 1) :
  ∃ (p : ℕ) [hp : fact (nat.prime p)] (s : ℝ) [hs : s > 0],
    ∀ (a : ℤ), f a = (@mul_ring_norm.padic p hp a)^s :=
begin
  obtain ⟨p, hp, h_aeq⟩ := a_eq_prime_ideal harc h_nontriv,
  refine ⟨p, hp, _⟩,
  cases hp,
  have fpgt0 := @norm_pos_of_ne_zero f _ (nat.cast_ne_zero.mpr (ne_of_gt hp.pos)),
  let s := (-real.log (f p : ℝ) / real.log p),
  have hs : s > 0,
  { refine div_pos _ (@real.log_pos p (by exact_mod_cast hp.one_lt)),
    { refine neg_pos.mpr ((real.log_neg_iff fpgt0).mpr _),
      have p_mem_a : (p : ℤ) ∈ ideal.span ({p} : set ℤ) := by rw ideal.mem_span_singleton,
      rw ←h_aeq at p_mem_a,
      exact p_mem_a, } },
  refine ⟨s, hs, _⟩,
  intro a,
  by_cases ha : a = 0,
  { simp_rw [ha, int.cast_zero, map_zero],
    exact (real.zero_rpow (norm_num.ne_zero_of_pos s hs)).symm },
  obtain ⟨b, hapb, hndiv⟩ := multiplicity.exists_eq_pow_mul_and_not_dvd (mult_finite hp ha),
  let m := (multiplicity (p : ℤ) a).get (mult_finite hp ha),
  have : f a = (f p) ^ m,
  { rw hapb,
    have hb : ↑b ∉ 𝔞 harc,
    { rw h_aeq,
      intro hmem,
      rw ideal.mem_span_singleton' at hmem,
      obtain ⟨k, hk⟩ := hmem,
      apply hndiv,
      rw dvd_iff_exists_eq_mul_left,
      refine ⟨k, hk.symm⟩ },
    dsimp only [𝔞] at hb,
    simp only [int.cast_id, submodule.mem_mk, set.mem_set_of_eq, not_lt] at hb,
    suffices h'' : f ((p : ℚ) ^ m * (b : ℚ)) = (f (p : ℚ)) ^ m,
    { convert h'',
      norm_cast },
    rw [f_mul_eq, le_antisymm (int_norm_le_one b harc) hb, mul_one, mul_eq_pow] },
  rw this,
  simp only [mul_ring_norm_eq_padic_norm, ha, padic_norm.eq_zpow_of_nonzero, ne.def,
    int.cast_eq_zero, not_false_iff, padic_val_rat.of_int, zpow_neg, zpow_coe_nat,
      rat.cast_inv, rat.cast_pow, rat.cast_coe_nat],
  unfold padic_val_int padic_val_nat,
  simp [ha, hp.ne_one, int.nat_abs_pos_of_ne_zero ha, multiplicity.int.nat_abs p a],
  have hppos : (p : ℝ) > 0 := nat.cast_pos.mpr (hp.pos),
  exact rearrange m hppos (norm_num.ne_zero_of_pos _ (@real.log_pos p (by exact_mod_cast hp.one_lt))) fpgt0,
end

lemma cast_pow_sub (r : ℝ) (a b : ℤ) : r ^ (a - b) = r ^ ((a : ℝ) - (b : ℝ)) := by norm_cast

lemma cast_pow (r : ℝ) (a : ℕ) : r ^ a = r ^ (a : ℝ) := by norm_cast

-- Extend this to ℚ using div_eq
lemma rat_val_eq (harc : is_nonarchimedean f) (h_nontriv : f ≠ 1) :
  ∃ (p : ℕ) [hp : fact (nat.prime p)] (s : ℝ) (hs : s > 0),
    ∀ (a : ℚ), f a = (@mul_ring_norm.padic p hp a)^s :=
begin
  obtain ⟨p, hp, s, hs, h_int⟩ := int_val_eq harc h_nontriv,
  use [p, hp, s, hs],
  intro a,
  by_cases ha : a = 0,
  { rw [ha, map_zero, map_zero],
    have hs' : s ≠ 0 := norm_num.ne_zero_of_pos s hs,
    exact (real.zero_rpow hs').symm },
  have hcast : f (a.denom) = (@mul_ring_norm.padic p hp a.denom) ^ s := h_int a.denom,
  rw [←rat.num_div_denom a, ring_norm.div_eq, h_int, hcast],
  simp [ha],
  unfold padic_val_rat,
  rw [cast_pow_sub, real.rpow_sub],
  unfold padic_norm,
  simp [rat.num_ne_zero_of_ne_zero ha, rat.denom_ne_zero a],
  rw [real.inv_rpow, real.inv_rpow],
  simp only [inv_div_inv],
  rw ←real.div_rpow,
  repeat {
    -- fact hp --> hp
    cases hp,
    rw cast_pow,
    exact real.rpow_nonneg_of_nonneg (le_of_lt (nat.cast_pos.mpr hp.pos)) _
  },
  cases hp,
  exact (nat.cast_pos.mpr hp.pos),
  norm_cast,
  exact rat.denom_ne_zero a,
end

-- Finish: hence f and padic are equivalent
lemma f_equiv_padic (harc : is_nonarchimedean f) (h_nontriv : f ≠ 1) :
 ∃ (p : ℕ) [hp : fact (nat.prime p)], mul_ring_norm.equiv f (@mul_ring_norm.padic p hp) :=
begin
  obtain ⟨p, hp, s, hs, h⟩ := rat_val_eq harc h_nontriv,
  use [p, hp, 1 / s],
  refine ⟨one_div_pos.mpr hs, _⟩,
  ext a,
  rw [h, ←real.rpow_mul],
  simp [norm_num.ne_zero_of_pos s hs],
  unfold mul_ring_norm.padic,
  simp only [map_nonneg]
end

end non_archimedean

section archimedean

-- This should be the same as `Sum_le`
lemma Sum_le' (n : ℕ) (ι : finset.Iio n → ℚ) :
  f (∑ i : finset.Iio n, ι i) ≤ ∑ i : finset.Iio n, f (ι i) :=
begin
  sorry
end

--First limit
lemma limit1 {N : ℝ} (hN : 0 < N) : filter.tendsto (λ n : ℕ, N ^ (1 / (n : ℝ))) filter.at_top (nhds 1) :=
begin
  rw ←real.exp_log hN,
  simp_rw [←real.exp_mul],
  refine real.tendsto_exp_nhds_0_nhds_1.comp _,
  simp_rw [mul_one_div],
  apply tendsto_const_div_at_top_nhds_0_nat
end

--Second limit
lemma limit2 (c : ℝ) (hc : 0 < c) : filter.tendsto (λ n : ℕ, (1 + (n : ℝ)*c)^(1 / (n : ℝ))) filter.at_top (nhds 1) :=
begin
  have cne0 : c ≠ 0 := ne_of_gt hc,
  have : (λ n : ℕ, (1+(n : ℝ)*c)^(1 / (n : ℝ))) = (λ (x : ℝ), x ^ (1 / ((1 / c) * x  + (- 1) / c))) ∘ (λ y : ℝ, 1 + c*y) ∘ coe,
  { ext n, simp, rw mul_add, rw ←mul_assoc, simp, rw div_eq_mul_inv, rw add_comm c⁻¹, rw add_assoc, rw [neg_mul, one_mul, add_right_neg, add_zero, inv_mul_cancel cne0, one_mul, mul_comm] },
  rw this,
  have : 1 / c ≠ 0 := one_div_ne_zero cne0,
  refine (tendsto_rpow_div_mul_add 1 (1 / c) (-1 / c) this.symm).comp _,
  have : filter.tendsto (λ y : ℝ, 1 + c*y) filter.at_top filter.at_top,
  { apply filter.tendsto_at_top_add_const_left,
    apply filter.tendsto.const_mul_at_top hc,
    intros x hx,
    exact hx },
  exact this.comp tendsto_coe_nat_at_top_at_top
end

--Potentially useful
example {c : ℝ} (hc : 0 < c) : filter.tendsto (λ n : ℕ, ((n : ℝ))^(1 / (n : ℝ))) filter.at_top (nhds 1) :=
begin
  have hf : (λ n : ℕ, (n : ℝ)^(1 / (n : ℝ))) = λ n : ℕ, (((λ x : ℝ, x^(1 / x)) ∘ coe) n),
  { ext, simp only [eq_self_iff_true] },
  rw hf,
  apply filter.tendsto.comp _ tendsto_coe_nat_at_top_at_top,
  exact tendsto_rpow_div,
  apply_instance,
end

lemma pow_mul_pow_le_max_pow {a b : ℝ} {m n : ℕ} (ha : 0 ≤ a) (hb : 0 ≤ b) : a^m * b^n ≤ max a b ^ (m + n) :=
begin
  rw pow_add,
  apply mul_le_mul,
  { exact pow_le_pow_of_le_left ha (le_max_left a b) m },
  { exact pow_le_pow_of_le_left hb (le_max_right a b) n },
  { exact pow_nonneg hb n },
  { apply pow_nonneg,
    rw le_max_iff,
    left,
    exact ha }
end

lemma inter_ineq {n : ℕ} (x y : ℚ) (hf : ∀ m : ℕ, f m ≤ 1) :
  f (x + y)^(n : ℝ) ≤ (n + 1 : ℝ) * max (f x) (f y)^n :=
begin
  norm_cast,
  rw [←mul_eq_pow, add_pow],
  apply le_trans (Sum_le f (n + 1)),
  suffices goal_1 : ∑ i in finset.range (n + 1), f ( x^i * y^(n - i) * (n.choose i) )
    = ∑ i in finset.range (n + 1), f (x ^ i) * f(y ^ (n - i)) * f (n.choose i),
  { rw goal_1,
    clear goal_1,
    calc ∑ i in finset.range (n + 1), f (x ^ i) * f(y ^ (n - i)) * f (n.choose i)
        ≤ ∑ i in finset.range (n + 1), f (x ^ i) * f (y ^ (n - i)) :
          begin
            apply finset.sum_le_sum,
            intros i hi,
            conv { to_rhs, rw ←mul_one (f (x ^ i) * f (y ^ (n - i))) },
            exact mul_le_mul_of_nonneg_left (hf _) (mul_nonneg (map_nonneg f _) (map_nonneg f _))
          end
    ... ≤ ∑ i in finset.range (n + 1), max (f x) (f y) ^ n :
          begin
            apply finset.sum_le_sum,
            intros i hi,
            have : i + (n - i) = n,
            { rw add_comm,
              apply nat.sub_add_cancel,
              simp at hi,
              linarith },
            conv { to_rhs, rw ←this },
            repeat { rw mul_eq_pow },
            exact pow_mul_pow_le_max_pow (map_nonneg f x) (map_nonneg f y),
          end
    ... = ↑(n + 1) * max (f x) (f y) ^ n : by simp, },
    { congr',
      ext,
      rw [f_mul_eq, f_mul_eq] }
end

lemma root_ineq {n : ℕ} (x y : ℚ) (hn : n ≠ 0) (hf : ∀ m : ℕ, f m ≤ 1) :
  f (x + y) ≤ (n + 1 : ℝ) ^ (1 / (n : ℝ)) * max (f x) (f y) :=
begin
  refine le_of_pow_le_pow n _ (nat.pos_of_ne_zero hn) _,
  { apply mul_nonneg,
    { apply real.rpow_nonneg_of_nonneg,
      norm_cast,
      linarith },
    { rw le_max_iff,
      left,
      exact map_nonneg f x } },
  { rw mul_pow,
    have : 0 ≤ (n : ℝ) + 1 := by { norm_cast, linarith },
    nth_rewrite 1 [←real.rpow_nat_cast],
    rw [←real.rpow_mul this, one_div],
    have : (n : ℝ) ≠ 0 := by { norm_cast, exact hn },
    rw [inv_mul_cancel this, real.rpow_one, ←real.rpow_nat_cast],
    exact inter_ineq x y hf }
end

-- A norm is non-archimedean iff it's bounded on the naturals
lemma non_archimedean_iff_nat_norm_bound : (∀ n : ℕ, f n ≤ 1) ↔ is_nonarchimedean f :=
begin
  split,
  { intros H x y,
    have lim : filter.tendsto (λ n : ℕ, (n + 1 : ℝ) ^ (1 / (n : ℝ)) * max (f x) (f y)) filter.at_top (nhds (max (f x) (f y))),
    { nth_rewrite 0 ←one_mul (max (f x) (f y)),
      apply filter.tendsto.mul_const (max (f x) (f y)),
      suffices goal_1 : (λ k : ℕ, ((k : ℝ) + 1) ^ (1 / (k : ℝ))) = (λ k : ℕ, (1 + (k : ℝ) * 1) ^ (1 / (k : ℝ))),
      { rw goal_1,
        clear goal_1,
        exact limit2 1 (real.zero_lt_one) },
      { ext k,
        simp,
        rw add_comm } },
    apply ge_of_tendsto lim _,
    simp only [filter.eventually_at_top, ge_iff_le],
    use 1,
    intros b hb,
    have : b ≠ 0 := nat.one_le_iff_ne_zero.mp hb,
    exact root_ineq x y this H },
  { intros hf n,
    exact nat_norm_le_one n hf }
end

lemma aux1 {n₀ : ℕ} (hf : ∃ n : ℕ, 1 < f n) (dn₀ : n₀ = nat.find hf) : 1 < n₀ :=
begin
  have hn₀ := nat.find_spec hf,
  rw ←dn₀ at hn₀,
  by_contra',
  rw lt_iff_not_ge at hn₀,
  interval_cases n₀,
  { apply hn₀,
    simp only [nat.cast_zero, map_zero, ge_iff_le, zero_le_one] },
  { apply hn₀,
    simp [f.map_one'] }
end

lemma list.map_with_index_append' {α M : Type*} [add_comm_monoid M]
  (K L : list α) (f : ℕ → α → M) :
  (K ++ L).map_with_index f = K.map_with_index f ++ L.map_with_index (λ i a, f (i + K.length) a) :=
begin
  induction K with a J IH generalizing f,
  { simp },
  { simp [IH (λ i, f (i+1)), add_assoc], }
end

-- This should be the same as `list.map_with_index_sum_to_finset_sum`
lemma list.map_with_index_sum_to_finset_sum' {β A : Type*} [add_comm_monoid A] {f : ℕ → β → A}
  {L : list β}  [inhabited β] : (L.map_with_index f).sum = ∑ i : finset.Iio L.length,
    f i ((L.nth_le i (finset.mem_Iio.1 i.2))) :=
begin
  sorry
end

lemma list.map_with_index_sum_to_finset_sum {β A : Type*} [add_comm_monoid A] {f : ℕ → β → A}
  {L : list β}  [inhabited β] : (L.map_with_index f).sum = ∑ i in finset.range L.length,
    f i ((L.nth i).get_or_else default) :=
begin
  apply list.reverse_rec_on L, -- the induction principle which works best
  { simp, },
  { intros M a IH,
    simp [list.map_with_index_append, IH],
    rw finset.sum_range_succ,
    congr' 1,
    { apply finset.sum_congr rfl,
      intros x hx,
      congr' 2,
      rw finset.mem_range at hx,
      rw list.nth_append hx, },
    { simp, } },
end

-- This is lemma 1.1
lemma aux2 {n₀ : ℕ} {α : ℝ} (hf : ∃ n : ℕ, 1 < f n)
  (dn₀ : n₀ = nat.find hf) (dα : α = real.log (f n₀) / real.log n₀) :
    ∀ n : ℕ, f n ≤ n ^ α :=
begin
  have : f n₀ = n₀ ^ α,
  { rw [dα, real.log_div_log],
    apply eq.symm,
    apply real.rpow_logb,
    { norm_cast,
      exact lt_trans zero_lt_one (aux1 hf dn₀) },
    { apply ne_of_gt,
      norm_cast,
      exact aux1 hf dn₀ },
    { have hn₀ := nat.find_spec hf,
      rw ←dn₀ at hn₀,
      exact lt_trans zero_lt_one hn₀ } },
  have hα : 0 < α,
  { rw dα,
    apply div_pos,
    { apply real.log_pos,
      rw dn₀,
      exact nat.find_spec hf },
    { apply real.log_pos,
      norm_cast,
      exact aux1 hf dn₀ } },
  let C : ℝ := ((n₀ : ℝ) ^ α) / ((n₀ : ℝ) ^ α - 1),
  have dc : C = ((n₀ : ℝ) ^ α) / ((n₀ : ℝ) ^ α - 1) := rfl,
  have hC : 0 < C,
  { rw dc,
    rw ← this,
    have hn₀ := nat.find_spec hf,
    rw ←dn₀ at hn₀,
    apply div_pos; linarith, }, -- easy to do
  suffices : ∀ n : ℕ, f n ≤ C * ((n : ℝ) ^ α),
  { intro n,
    have limit' : filter.tendsto (λ N : ℕ, C ^ (1 / (N : ℝ))) filter.at_top (nhds 1),
    { exact limit1 hC, }, --someone good at analysis
    have limit'' : filter.tendsto
      (λ N : ℕ, (C ^ (1 / (N : ℝ))) * (n ^ α)) filter.at_top (nhds (n ^ α)),
    { have := filter.tendsto.mul_const (↑n ^ α) limit',
      simp at this,
      simp,
      exact this, }, --following from limit'
    have stupid : (0 : ℝ) ≤ n := by norm_cast; exact zero_le n, -- very easy
    have aux : ∀ N : ℕ, (f (n)) ^ (N : ℝ) ≤ C * ((n ^ α) ^ (N : ℝ)),
    { intro N,
      rw ←real.rpow_mul stupid,
      nth_rewrite 1 mul_comm,
      rw real.rpow_mul stupid,
      norm_cast,
      rw ←mul_eq_pow,
      specialize this (n ^ N),
      norm_cast,
      exact this, },
    have aux1 : ∀ N : ℕ, 0 < N → f (n) ≤ (C ^ (1 / (N : ℝ))) * (n ^ α),
    { intros N hN,
      refine le_of_pow_le_pow N _ hN _,
      { apply mul_nonneg,
        { apply le_of_lt,
          exact real.rpow_pos_of_pos hC _, },
        { exact real.rpow_nonneg_of_nonneg stupid _, } },
      { rw mul_pow,
        repeat {rw [←real.rpow_nat_cast]},
        rw [←real.rpow_mul (le_of_lt hC), one_div],
        have : (N : ℝ) ≠ 0,
        { norm_cast,
          rw push_neg.not_eq,
          exact ne_of_gt hN, },
        rw [inv_mul_cancel this, real.rpow_one],
        exact aux N, } },  --take nth root on both side
    apply ge_of_tendsto limit'' _,
    simp only [filter.eventually_at_top, ge_iff_le],
    use 1,
    intros b hb,
    have : 0 < b := (by linarith),
    exact aux1 b this, },
  intro n,
  by_cases n = 0,
  { subst h,
    simp [hα],
    nlinarith [hC, real.zero_rpow_nonneg α] },
  have length_lt_one : 0 ≤ ((n₀.digits n).length : ℝ) - 1, -- Not sure whether this is useful or not
  { norm_num,
    sorry}, -- should be easy `digits_ne_nil_iff_ne_zero` might be useful
  conv_lhs { rw ←nat.of_digits_digits n₀ n },
  rw nat.of_digits_eq_sum_map_with_index,
  rw list.map_with_index_sum_to_finset_sum',
  simp only [nat.cast_sum, nat.cast_mul, nat.cast_pow],
  apply le_trans (Sum_le' (n₀.digits n).length _),
  have aux' : 2 ≤ n₀ := by linarith [aux1 hf dn₀],
  suffices goal_1 : ∑ i : finset.Iio (n₀.digits n).length,
    f (((((n₀.digits n).nth_le i (finset.mem_Iio.1 i.2))) : ℚ)
      * (n₀ : ℚ) ^ (i : ℕ)) = ∑ i : finset.Iio (n₀.digits n).length,
        f (((n₀.digits n).nth_le i (finset.mem_Iio.1 i.2)))
          * (f n₀) ^ (i : ℕ),
  { rw goal_1,
    clear goal_1,
    have coef_ineq : ∀ i : finset.Iio (n₀.digits n).length,
      f (((n₀.digits n).nth_le i (finset.mem_Iio.1 i.2))) ≤ 1,
    { intro i,
      have : ((n₀.digits n).nth_le i (finset.mem_Iio.1 i.2)) < n₀,
      { have aux'' : ((n₀.digits n).nth_le i (finset.mem_Iio.1 i.2)) ∈ n₀.digits n,
        { exact (nat.digits n₀ n).nth_le_mem ↑i (finset.mem_Iio.mp i.property) },
        exact nat.digits_lt_base aux' aux'', },
      apply le_of_not_gt,
      subst dn₀,
      rw gt_iff_lt,
      exact nat.find_min hf this },
    rw this,
    have goal1 : ∑ (i : (finset.Iio (n₀.digits n).length)),
      f ((n₀.digits n).nth_le ↑i (finset.mem_Iio.1 i.2)) * (n₀ ^ α) ^ (i : ℕ) ≤
        ∑ (i : (finset.Iio (n₀.digits n).length)), (n₀ ^ α) ^ (i : ℕ),
    {sorry},
    apply le_trans goal1,
    clear goal1,
    have goal2 : (∑ i : (finset.Iio (n₀.digits n).length), ((n₀ : ℝ) ^ α) ^ (i : ℕ)) =
    (((n₀ : ℝ) ^ (α * ((n₀.digits n).length - 1))) *
      ∑ i : (finset.Iio (n₀.digits n).length), ((n₀ : ℝ) ^ -α) ^ (i : ℕ)),
    {sorry},
    rw goal2,
    clear goal2,
    have goal3_aux : ∑ (i : (finset.Iio (n₀.digits n).length)),
      ((n₀ : ℝ) ^ -α) ^ (i : ℕ) ≤ ∑'i : ℕ, (1 / ((n₀ : ℝ) ^ α)) ^ i,
    {sorry},
    have goal3_aux' : 0 ≤ (n₀ : ℝ) ^ (α * (((n₀.digits n).length - 1))),
    {sorry}, -- easy
    have goal3 : ((n₀ : ℝ) ^ (α * (((n₀.digits n).length - 1))))
      * (∑ (i : (finset.Iio (n₀.digits n).length)), ((n₀ : ℝ) ^ -α) ^ (i : ℕ))
        ≤ ((n₀ : ℝ) ^ (α * (((n₀.digits n).length - 1)))) *
          (∑'i : ℕ, (1 / ((n₀ : ℝ) ^ α)) ^ i),
    {sorry}, -- easy here
    apply le_trans goal3,
    clear goal3_aux goal3_aux' goal3,
    have goal4 : ∑'i : ℕ, (1 / ((n₀ : ℝ) ^ α)) ^ i = C,
    {sorry}, -- `tsum_geometric_of_abs_lt_1` is useful here.
    rw goal4,
    clear goal4,
    rw mul_comm,
    suffices : (n₀ : ℝ) ^ (α * (((n₀.digits n).length - 1))) ≤ (n : ℝ) ^ α,
    { nlinarith },
    have goal : (n₀ : ℝ) ^ (((n₀.digits n).length : ℝ) - 1) ≤ (n : ℝ),
    { have h' := nat.base_pow_length_digits_le n₀ n aux' h,
      have h'' : (n₀ : ℝ) ^ ((n₀.digits n).length : ℝ) ≤ (n₀ : ℝ) * (n : ℝ),
      { norm_cast,
        exact h' },
      have aux'' : 0 < (n₀ : ℝ) := by norm_cast;linarith,
      have stupid : (n₀ : ℝ) ≠ 0 := by norm_cast;linarith,
      have h''' : 0 ≤ (n₀ : ℝ) ^ (-(1 : ℝ)),
      { rw real.rpow_neg_one,
        have stupid2 : 0 ≤ (n₀ : ℝ)⁻¹ * n₀ := by simp [inv_mul_cancel stupid],
        exact nonneg_of_mul_nonneg_left stupid2 aux'' },
      have h'''' := mul_le_mul_of_nonneg_left h'' h''',
      rw ←real.rpow_add aux'' _ _ at h'''',
      rw add_comm at h'''',
      rw ←mul_assoc at h'''',
      apply le_trans h'''',
      rw real.rpow_neg_one,
      rw inv_mul_cancel stupid,
      linarith },
    have stupid : (0 : ℝ) ≤ n₀ := sorry, -- easy
    rw mul_comm,
    rw real.rpow_mul stupid,
    have stupid2 : 0 ≤ (n₀ : ℝ) ^ (((n₀.digits n).length : ℝ) - 1) := sorry, --easy
    exact real.rpow_le_rpow stupid2 goal (le_of_lt hα) },
  { congr',
    ext,
    rw [f_mul_eq, mul_eq_pow] }
end

-- This is lemma 1.2 (this looks hard btw)
lemma aux3 {n₀ : ℕ} {α : ℝ} (hf : ∃ n : ℕ, 1 < f n)
  (dn₀ : n₀ = nat.find hf) (dα : α = real.log (f n₀) / real.log n₀) :
    ∀ n : ℕ, (n ^ α : ℝ) ≤ f n :=
begin
  have hα₀ : 0 < α,
  { rw dα,
    apply div_pos,
    { apply real.log_pos,
      rw dn₀,
      exact nat.find_spec hf },
    { apply real.log_pos,
      norm_cast,
      exact aux1 hf dn₀ } },
  have hα : 0 ≤ α := by linarith,
  have hn₀ : 2 ≤ n₀ := by linarith [aux1 hf dn₀],
  have : f n₀ = n₀ ^ α := sorry, -- same proof as above
  let C : ℝ := (1 - (1 - 1 / n₀) ^ α),
  have hC : 0 ≤ C,
  { dsimp only [C],
    field_simp,
    apply real.rpow_le_one _ _ hα,
    { field_simp,

      sorry},
    {
      sorry} },
  suffices : ∀ n : ℕ, C * ((n : ℝ) ^ α) ≤ f n,
  {sorry}, -- This should be almost the same as above
  intros n,
  by_cases hn : n = 0,
  { subst hn,
    simp only [map_zero, algebra_map.coe_zero, map_zero],
    rw real.zero_rpow,
    { rw mul_zero },
    linarith },
  have length_lt_one : 1 ≤ (n₀.digits n).length,
  { by_contra goal,
    simp only [not_le, nat.lt_one_iff] at goal,
    rw [list.length_eq_zero, nat.digits_eq_nil_iff_eq_zero] at goal,
    contradiction },
  have h₁ : f ((n₀ : ℚ) ^ ((n₀.digits n).length))
    - f (((n₀ : ℚ) ^ ((n₀.digits n).length)) - n) ≤ f n,
  { have goal := abs_sub_map_le_sub f ((n₀ : ℚ) ^ ((n₀.digits n).length)) (((n₀ : ℚ) ^ ((n₀.digits n).length)) - n),
    simp only [map_pow, sub_sub_cancel] at goal,
    apply le_trans _ goal,
    rw map_pow,
    exact le_abs_self _ },
  apply le_trans' h₁,
  rw [mul_eq_pow, this],
  have h := aux2 hf dn₀ dα,
  specialize h ((n₀ ^ ((n₀.digits n).length)) - n),
  have hn₁ : n ≤ n₀ ^ (n₀.digits n).length := by linarith [@nat.lt_base_pow_length_digits n₀ n hn₀],
  have h₂ : ((n₀ : ℝ) ^ α) ^ (n₀.digits n).length - ((n₀ ^ (n₀.digits n).length - n) : ℚ) ^ α ≤
  ((n₀ : ℝ) ^ α) ^ (n₀.digits n).length - f ((n₀ : ℚ) ^ (n₀.digits n).length - (n : ℚ)),
  { rw sub_le_sub_iff_left,
    push_cast at h,
    simp only [rat.cast_sub, rat.cast_pow, rat.cast_coe_nat],
    exact h },
  apply le_trans' h₂,
  clear h₂,
  simp only [rat.cast_sub, rat.cast_pow, rat.cast_coe_nat],
  have h₃ : ((n₀ : ℝ) ^ α) ^ (n₀.digits n).length - ((n₀ : ℝ) ^ (n₀.digits n).length - (n₀ : ℝ) ^ ((n₀.digits n).length - 1)) ^ α ≤
    ((n₀ : ℝ) ^ α) ^ (n₀.digits n).length - ((n₀ : ℝ) ^ (n₀.digits n).length - (n : ℝ)) ^ α,
  { rw sub_le_sub_iff_left,
    apply real.rpow_le_rpow _ _ hα,
    { field_simp,
      norm_cast,
      exact hn₁ },
    { field_simp,
      norm_cast,
      rw ← nat.pow_div length_lt_one,
      { simp only [pow_one],
        exact nat.div_le_of_le_mul (nat.base_pow_length_digits_le n₀ n hn₀ hn) },
      linarith } },
  apply le_trans' h₃,
  clear h₃,
  have h₄ : ((n₀ : ℝ) ^ α) ^ (n₀.digits n).length -
    ((n₀ : ℝ) ^ (n₀.digits n).length - (n₀ : ℝ) ^ ((n₀.digits n).length - 1)) ^ α
      = (((n₀ : ℝ) ^ α) ^ (n₀.digits n).length) * (1 - (1 - 1 / n₀) ^ α),
  { rw mul_sub,
    rw mul_one,
    rw sub_right_inj,
    repeat {rw ←real.rpow_nat_cast},
    rw ←real.rpow_mul,  -- This looks stupid here, as I am looking for (a ^ b) ^ c = (a ^ c) ^ b
    { nth_rewrite 1 mul_comm,
      rw real.rpow_mul,
      { rw ←real.mul_rpow,
        { rw mul_sub,
          rw mul_one,
          rw nat.cast_sub length_lt_one,
          rw real.rpow_sub,
          { ring_nf,
            simp only [algebra_map.coe_one, real.rpow_one] },
          norm_cast,
          linarith [aux1 hf dn₀] },
        { norm_cast,
          linarith [nat.one_le_pow ((n₀.digits n).length)
            n₀ (by linarith [aux1 hf dn₀])] },
        { simp only [sub_nonneg],
          rw one_div_le,
          { simp only [div_self, ne.def, one_ne_zero, not_false_iff, nat.one_le_cast],
            linarith [aux1 hf dn₀] },
          { norm_cast,
            linarith [aux1 hf dn₀] },
          { linarith } } },
      norm_cast,
      exact nat.zero_le n₀ },
    norm_cast,
    exact nat.zero_le n₀ },
  rw h₄,
  clear h₄,
  change (1 - (1 - 1 / (n₀ : ℝ)) ^ α) with C,
  nth_rewrite 1 mul_comm,
  apply mul_le_mul_of_nonneg_left _ hC,
  suffices goal : (n : ℝ )^ α ≤ ((n₀ : ℝ) ^ (n₀.digits n).length) ^ α,
  { rw ←real.rpow_nat_cast at goal ⊢,
    rw ←real.rpow_mul, -- This looks stupid here, as I am looking for (a ^ b) ^ c = (a ^ c) ^ b
    { rw mul_comm,
      rw real.rpow_mul,
      { exact goal },
      norm_cast,
      exact nat.zero_le n₀ },
    norm_cast,
    exact nat.zero_le n₀ },
  apply real.rpow_le_rpow,
  { norm_cast,
    exact nat.zero_le n },
  { norm_cast,
    linarith [@nat.lt_base_pow_length_digits _ n hn₀] },
  { exact hα }
end

lemma archimedean_case (hf : ¬ is_nonarchimedean f) : mul_ring_norm.equiv f mul_ring_norm.real :=
begin
  rw ←non_archimedean_iff_nat_norm_bound at hf,
  simp only [not_forall, not_le] at hf,
  let n₀ : ℕ := nat.find hf,
  have dn₀ : n₀ = nat.find hf := rfl,
  let α : ℝ := real.log (f n₀) / real.log n₀,
  have hα : α =  real.log (f n₀) / real.log n₀ := rfl,
  have h₃ : ∀ (n : ℕ), f (n : ℚ) = (n : ℝ) ^ α,
  { intro n,
    linarith [aux3 hf dn₀ hα n, aux2 hf dn₀ hα n] },
  have h₄ : ∀ (n : ℕ), f (n : ℚ) = |n| ^ α,
  { intro n,
    rw nat.abs_cast n,
    exact h₃ n },
  apply mul_ring_norm.equiv_symm _ _,
  refine ⟨α, _, _⟩,
  { rw hα,
    apply div_pos,
    { apply real.log_pos,
      exact nat.find_spec hf },
    { apply real.log_pos,
      norm_cast,
      exact aux1 hf dn₀ } },
  { ext,
    rw mul_ring_norm_eq_abs,
    rw ←rat.num_div_denom x,
    norm_cast,
    rw ←rat.coe_int_div_eq_mk,
    rw abs_div,
    push_cast,
    rw ring_norm.div_eq,
    { rw real.div_rpow,
      { congr,
        { cases x.num with b b,
          { simp only [int.of_nat_eq_coe, int.cast_coe_nat],
            exact (h₄ b).symm },
          { simp only [int.cast_neg_succ_of_nat, nat.cast_add, algebra_map.coe_one,
              neg_add_rev],
            rw ←abs_neg,
            rw ←map_neg_eq_map,
            simp only [neg_add_rev, neg_neg],
            norm_cast,
            exact (h₃ (b + 1)).symm } },
        { exact (h₄ x.denom).symm } },
      { exact norm_nonneg ((x.num) : ℝ) },
      { exact norm_nonneg ((x.denom) : ℝ) } },
    { norm_cast,
      exact rat.denom_ne_zero x } },
end

end archimedean

/-- Ostrowski's Theorem -/
theorem rat_ring_norm_p_adic_or_real (f : mul_ring_norm ℚ) (hf_nontriv : f ≠ 1) :
  (mul_ring_norm.equiv f mul_ring_norm.real) ∨
  ∃ (p : ℕ) [hp : fact (nat.prime p)], mul_ring_norm.equiv f (@mul_ring_norm.padic p hp) :=
begin
    by_cases bdd : ∀ z : ℕ, f z ≤ 1,
    { right, /- p-adic case -/
      rw [non_archimedean_iff_nat_norm_bound] at bdd,
      exact f_equiv_padic bdd hf_nontriv },
    { left,
      rw non_archimedean_iff_nat_norm_bound at bdd,
      exact archimedean_case bdd, /- Euclidean case -/ }
end


import SciLean.Mathlib.Convenient.Basic

namespace SciLean

namespace TensorProduct

  variable {X Y} [Vec X] [Vec Y]

  def Repr (X Y : Type) := Array (X×Y)

  -- TODO: Define equivalence of two formal sums `∑ i, x i ⊗ y i`
  def Equiv (u v : Repr X Y) : Prop := sorry

end TensorProduct

-- Algebraic tensor product
-- If I'm not mistaken, for X Y convenient vector spaces, the algebraic tensor product X ⊗ Y is again a convenient vector space. It is not some completion of the algebraic tensor product.
-- TODO: make this as Graded Quotient
def TensorProduct (X Y : Type) [Vec X] [Vec Y] : Type := Quot (TensorProduct.Equiv (X:=X) (Y:=Y))

namespace TensorProduct

  def tprod {X Y} [Vec X] [Vec Y] (x : X) (y : Y) : TensorProduct X Y := Quot.mk _ #[(x,y)]

  instance {X Y} [Vec X] [Vec Y] : OTimes X Y (TensorProduct X Y) := ⟨⟩
  instance {X Y} [Vec X] [Vec Y] (x : X) (y : Y) : OTimes x y (tprod x y) := ⟨⟩

  variable {X Y Z} [Vec X] [Vec Y] [Vec Z]

  open Classical in
  noncomputable
  def repr  (xy : X ⊗ Y)
    : (n : Nat) × (Fin n → X) × (Fin n → Y) :=
      let r : Array (X×Y) := choose (Quot.exists_rep xy)
      let n := r.size
      let x := λ i => r[i].1
      let y := λ i => r[i].2
      ⟨n,x,y⟩

  structure is_linear  (f : X → Y) : Prop where
    add  : ∀ x y, f (x + y) = f x + f y
    smul : ∀ (s : ℝ) x, f (s•x) = s • (f x)

  /-- Extends bilinear map `X → Y → Z` to `X ⊗ Y → Z`. -/
  def map' (f : X → Y → Z) (h : is_linear f ∧ ∀ x, is_linear (f x)) (xy : X ⊗ Y) : Z :=
    Quot.lift (λ xy : Array (X×Y) => ∑ i : Fin xy.size, (f xy[i].1 xy[i].2)) sorry_proof xy

  @[simp] theorem map'_tprod (f : X → Y → Z) (h : is_linear f ∧ ∀ x, is_linear (f x)) (x : X) (y : Y)
    : map' f h (x⊗y) = f x y := sorry_proof

  instance : Neg (X⊗Y) := ⟨λ xy  =>
    xy.liftOn (λ xy : Array (X×Y) => Quot.mk _ <| xy.map (λ (x,y) => (-x,y))) sorry_proof⟩

  instance : Add (X⊗Y) := ⟨λ xy xy' =>
    xy.liftOn (λ xy : Array (X×Y) =>
    xy'.liftOn (λ xy' : Array (X×Y) => Quot.mk _ (xy.append xy')) sorry_proof) sorry_proof⟩

  instance : Sub (X⊗Y) := ⟨λ xy xy' => xy + (-xy')⟩

  -- We scale x and y by the same amount in the hope of better numerical stability
  instance : SMul ℝ (X⊗Y) := ⟨λ r xy =>
    let s  := if 0 ≤ r then 1 else 0
    let r' := if 0 ≤ r then r.sqrt else (-r).sqrt
    xy.liftOn (λ xy =>
      Quot.mk _ <| xy.map (λ (x,y) => ((s*r')•x, r'•y))) sorry_proof⟩

  instance : Zero (X⊗Y) := ⟨(Quot.mk _ (#[] : Array (X×Y)))⟩

  instance : Vec (X⊗Y) := Vec.mkSorryProofs


/-
Copyright (c) 2022 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import analysis.complex.cauchy_integral
import analysis.calculus.fderiv_analytic
import analysis.normed_space.completion

/-!
# Liouville's theorem

In this file we prove Liouville's theorem: if `f : E → F` is complex differentiable on the whole
space and its range is bounded, then the function is a constant. Various versions of this theorem
are formalized in `differentiable.apply_eq_apply_of_bounded`,
`differentiable.exists_const_forall_eq_of_bounded`, and
`differentiable.exists_eq_const_of_bounded`.

The proof is based on the Cauchy integral formula for the derivative of an analytic function, see
`complex.deriv_eq_smul_circle_integral`.
-/

open topological_space metric set filter asymptotics function measure_theory
open_locale topological_space filter nnreal real

universes u v
variables {E : Type u} [normed_group E] [normed_space ℂ E]
  {F : Type v} [normed_group F] [normed_space ℂ F]

local postfix `̂`:100 := uniform_space.completion

namespace complex

/-- If `f` is complex differentiable on an open disc with center `c` and radius `R > 0` and is
continuous on its closure, then `f' c` can be represented as an integral over the corresponding
circle.

TODO: add a version for `w ∈ metric.ball c R`.

TODO: add a version for higher derivatives. -/
lemma deriv_eq_smul_circle_integral [complete_space F] {R : ℝ} {c : ℂ} {f : ℂ → F} (hR : 0 < R)
  (hf : diff_cont_on_cl ℂ f (ball c R)) :
  deriv f c = (2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - c) ^ (-2 : ℤ) • f z :=
begin
  lift R to ℝ≥0 using hR.le,
  refine (hf.has_fpower_series_on_ball hR).has_fpower_series_at.deriv.trans _,
  simp only [cauchy_power_series_apply, one_div, zpow_neg, pow_one, smul_smul, zpow_two, mul_inv]
end

lemma norm_deriv_le_aux [complete_space F] {c : ℂ} {R C : ℝ} {f : ℂ → F} (hR : 0 < R)
  (hf : diff_cont_on_cl ℂ f (ball c R)) (hC : ∀ z ∈ sphere c R, ∥f z∥ ≤ C) :
  ∥deriv f c∥ ≤ C / R :=
begin
  have : ∀ z ∈ sphere c R, ∥(z - c) ^ (-2 : ℤ) • f z∥ ≤ C / (R * R),
    from λ z (hz : abs (z - c) = R), by simpa [-mul_inv_rev, norm_smul, hz, zpow_two,
      ←div_eq_inv_mul] using (div_le_div_right (mul_pos hR hR)).2 (hC z hz),
  calc ∥deriv f c∥ = ∥(2 * π * I : ℂ)⁻¹ • ∮ z in C(c, R), (z - c) ^ (-2 : ℤ) • f z∥ :
    congr_arg norm (deriv_eq_smul_circle_integral hR hf)
  ... ≤ R * (C / (R * R)) :
    circle_integral.norm_two_pi_I_inv_smul_integral_le_of_norm_le_const hR.le this
  ... = C / R : by rw [mul_div_left_comm, div_self_mul_self', div_eq_mul_inv]
end

/-- If `f` is complex differentiable on an open disc of radius `R > 0`, is continuous on its
closure, and its values on the boundary circle of this disc are bounded from above by `C`, then the
norm of its derivative at the center is at most `C / R`. -/
lemma norm_deriv_le_of_forall_mem_sphere_norm_le {c : ℂ} {R C : ℝ} {f : ℂ → F} (hR : 0 < R)
  (hd : diff_cont_on_cl ℂ f (ball c R)) (hC : ∀ z ∈ sphere c R, ∥f z∥ ≤ C) :
  ∥deriv f c∥ ≤ C / R :=
begin
  set e : F →L[ℂ] F̂ := uniform_space.completion.to_complL,
  have : has_deriv_at (e ∘ f) (e (deriv f c)) c,
    from e.has_fderiv_at.comp_has_deriv_at c
      (hd.differentiable_at is_open_ball $ mem_ball_self hR).has_deriv_at,
  calc ∥deriv f c∥ = ∥deriv (e ∘ f) c∥ :
    by { rw this.deriv, exact (uniform_space.completion.norm_coe _).symm }
  ... ≤ C / R :
    norm_deriv_le_aux hR (e.differentiable.comp_diff_cont_on_cl hd)
      (λ z hz, (uniform_space.completion.norm_coe _).trans_le (hC z hz))
end

/-- An auxiliary lemma for Liouville's theorem `differentiable.apply_eq_apply_of_bounded`. -/
lemma liouville_theorem_aux {f : ℂ → F} (hf : differentiable ℂ f)
  (hb : bounded (range f)) (z w : ℂ) : f z = f w :=
begin
  suffices : ∀ c, deriv f c = 0, from is_const_of_deriv_eq_zero hf this z w,
  clear z w, intro c,
  obtain ⟨C, C₀, hC⟩ : ∃ C > (0 : ℝ), ∀ z, ∥f z∥ ≤ C,
  { rcases bounded_iff_forall_norm_le.1 hb with ⟨C, hC⟩,
    exact ⟨max C 1, lt_max_iff.2 (or.inr zero_lt_one),
      λ z, (hC (f z) (mem_range_self _)).trans (le_max_left _ _)⟩ },
  refine norm_le_zero_iff.1 (le_of_forall_le_of_dense $ λ ε ε₀, _),
  calc ∥deriv f c∥ ≤ C / (C / ε) :
    norm_deriv_le_of_forall_mem_sphere_norm_le (div_pos C₀ ε₀) hf.diff_cont_on_cl (λ z _, hC z)
  ... = ε : div_div_cancel' C₀.lt.ne'
end

end complex

namespace differentiable

open complex

/-- **Liouville's theorem**: a complex differentiable bounded function `f : E → F` is a constant. -/
lemma apply_eq_apply_of_bounded {f : E → F} (hf : differentiable ℂ f) (hb : bounded (range f))
  (z w : E) : f z = f w :=
begin
  set g : ℂ → F := f ∘ (λ t : ℂ, t • (w - z) + z),
  suffices : g 0 = g 1, by simpa [g],
  apply liouville_theorem_aux,
  exacts [hf.comp ((differentiable_id.smul_const (w - z)).add_const z),
    hb.mono (range_comp_subset_range _ _)]
end

/-- **Liouville's theorem**: a complex differentiable bounded function is a constant. -/
lemma exists_const_forall_eq_of_bounded {f : E → F} (hf : differentiable ℂ f)
  (hb : bounded (range f)) : ∃ c, ∀ z, f z = c :=
⟨f 0, λ z, hf.apply_eq_apply_of_bounded hb _ _⟩

/-- **Liouville's theorem**: a complex differentiable bounded function is a constant. -/
lemma exists_eq_const_of_bounded {f : E → F} (hf : differentiable ℂ f)
  (hb : bounded (range f)) : ∃ c, f = const E c :=
(hf.exists_const_forall_eq_of_bounded hb).imp $ λ c, funext

end differentiable


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

! This file was ported from Lean 3 source module ring_theory.nakayama
! leanprover-community/mathlib commit 5cfb8dbcc9965bd6270947f4643172c20979245a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.RingTheory.Noetherian
import Mathbin.RingTheory.JacobsonIdeal

/-!
# Nakayama's lemma

This file contains some alternative statements of Nakayama's Lemma as found in
[Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV).

## Main statements

* `submodule.eq_smul_of_le_smul_of_le_jacobson` - A version of (2) in
  [Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV).,
  generalising to the Jacobson of any ideal.
* `submodule.eq_bot_of_le_smul_of_le_jacobson_bot` - Statement (2) in
  [Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV).

* `submodule.smul_sup_eq_smul_sup_of_le_smul_of_le_jacobson` - A version of (4) in
  [Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV).,
  generalising to the Jacobson of any ideal.
* `submodule.smul_sup_eq_of_le_smul_of_le_jacobson_bot` - Statement (4) in
  [Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV).

Note that a version of Statement (1) in
[Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV) can be found in
`ring_theory/noetherian` under the name
`submodule.exists_sub_one_mem_and_smul_eq_zero_of_fg_of_le_smul`

## References
* [Stacks: Nakayama's Lemma](https://stacks.math.columbia.edu/tag/00DV)

## Tags
Nakayama, Jacobson
-/

variable {R M : Type _} [CommRing R] [AddCommGroup M] [Module R M]

open Ideal

namespace Submodule

/-- *Nakayama's Lemma** - A slightly more general version of (2) in
[Stacks 00DV](https://stacks.math.columbia.edu/tag/00DV).
See also `eq_bot_of_le_smul_of_le_jacobson_bot` for the special case when `J = ⊥`.  -/
theorem eq_smul_of_le_smul_of_le_jacobson {I J : Ideal R} {N : Submodule R M} (hN : N.Fg)
    (hIN : N ≤ I • N) (hIjac : I ≤ jacobson J) : N = J • N :=
  by
  refine' le_antisymm _ (Submodule.smul_le.2 fun _ _ _ => Submodule.smul_mem _ _)
  intro n hn
  cases' Submodule.exists_sub_one_mem_and_smul_eq_zero_of_fg_of_le_smul I N hN hIN with r hr
  cases' exists_mul_sub_mem_of_sub_one_mem_jacobson r (hIjac hr.1) with s hs
  have : n = -(s * r - 1) • n := by
    rw [neg_sub, sub_smul, mul_smul, hr.2 n hn, one_smul, smul_zero, sub_zero]
  rw [this]
  exact Submodule.smul_mem_smul (Submodule.neg_mem _ hs) hn
#align submodule.eq_smul_of_le_smul_of_le_jacobson Submodule.eq_smul_of_le_smul_of_le_jacobson

/-- *Nakayama's Lemma** - Statement (2) in
[Stacks 00DV](https://stacks.math.columbia.edu/tag/00DV).
See also `eq_smul_of_le_smul_of_le_jacobson` for a generalisation
to the `jacobson` of any ideal -/
theorem eq_bot_of_le_smul_of_le_jacobson_bot (I : Ideal R) (N : Submodule R M) (hN : N.Fg)
    (hIN : N ≤ I • N) (hIjac : I ≤ jacobson ⊥) : N = ⊥ := by
  rw [eq_smul_of_le_smul_of_le_jacobson hN hIN hIjac, Submodule.bot_smul]
#align submodule.eq_bot_of_le_smul_of_le_jacobson_bot Submodule.eq_bot_of_le_smul_of_le_jacobson_bot

/-- *Nakayama's Lemma** - A slightly more general version of (4) in
[Stacks 00DV](https://stacks.math.columbia.edu/tag/00DV).
See also `smul_sup_eq_of_le_smul_of_le_jacobson_bot` for the special case when `J = ⊥`.  -/
theorem smul_sup_eq_smul_sup_of_le_smul_of_le_jacobson {I J : Ideal R} {N N' : Submodule R M}
    (hN' : N'.Fg) (hIJ : I ≤ jacobson J) (hNN : N ⊔ N' ≤ N ⊔ I • N') : N ⊔ I • N' = N ⊔ J • N' :=
  by
  have hNN' : N ⊔ N' = N ⊔ I • N' :=
    le_antisymm hNN (sup_le_sup_left (Submodule.smul_le.2 fun _ _ _ => Submodule.smul_mem _ _) _)
  have h_comap := Submodule.comap_injective_of_surjective (LinearMap.range_eq_top.1 N.range_mkq)
  have : (I • N').map N.mkq = N'.map N.mkq :=
    by
    rw [← h_comap.eq_iff]
    simpa [comap_map_eq, sup_comm, eq_comm] using hNN'
  have :=
    @Submodule.eq_smul_of_le_smul_of_le_jacobson _ _ _ _ _ I J (N'.map N.mkq) (hN'.map _)
      (by rw [← map_smul'', this] <;> exact le_rfl) hIJ
  rw [← map_smul'', ← h_comap.eq_iff, comap_map_eq, comap_map_eq, Submodule.ker_mkQ, sup_comm,
    hNN'] at this
  rw [this, sup_comm]
#align submodule.smul_sup_eq_smul_sup_of_le_smul_of_le_jacobson Submodule.smul_sup_eq_smul_sup_of_le_smul_of_le_jacobson

/-- *Nakayama's Lemma** - Statement (4) in
[Stacks 00DV](https://stacks.math.columbia.edu/tag/00DV).
See also `smul_sup_eq_smul_sup_of_le_smul_of_le_jacobson` for a generalisation
to the `jacobson` of any ideal -/
theorem smul_sup_le_of_le_smul_of_le_jacobson_bot {I : Ideal R} {N N' : Submodule R M} (hN' : N'.Fg)
    (hIJ : I ≤ jacobson ⊥) (hNN : N ⊔ N' ≤ N ⊔ I • N') : I • N' ≤ N := by
  rw [← sup_eq_left, smul_sup_eq_smul_sup_of_le_smul_of_le_jacobson hN' hIJ hNN, bot_smul,
    sup_bot_eq]
#align submodule.smul_sup_le_of_le_smul_of_le_jacobson_bot Submodule.smul_sup_le_of_le_smul_of_le_jacobson_bot

end Submodule