/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov, Abhimanyu Pallavi Sudhir
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.filter.basic
import Mathlib.algebra.module.pi
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4 u_5 u_6 u_7

namespace Mathlib

/-!
# Germ of a function at a filter

The germ of a function `f : α → β` at a filter `l : filter α` is the equivalence class of `f`
with respect to the equivalence relation `eventually_eq l`: `f ≈ g` means `∀ᶠ x in l, f x = g x`.

## Main definitions

We define

* `germ l β` to be the space of germs of functions `α → β` at a filter `l : filter α`;
* coercion from `α → β` to `germ l β`: `(f : germ l β)` is the germ of `f : α → β`
  at `l : filter α`; this coercion is declared as `has_coe_t`, so it does not require an explicit
  up arrow `↑`;
* coercion from `β` to `germ l β`: `(↑c : germ l β)` is the germ of the constant function
  `λ x:α, c` at a filter `l`; this coercion is declared as `has_lift_t`, so it requires an explicit
  up arrow `↑`, see [TPiL][TPiL_coe] for details.
* `map (F : β → γ) (f : germ l β)` to be the composition of a function `F` and a germ `f`;
* `map₂ (F : β → γ → δ) (f : germ l β) (g : germ l γ)` to be the germ of `λ x, F (f x) (g x)`
  at `l`;
* `f.tendsto lb`: we say that a germ `f : germ l β` tends to a filter `lb` if its representatives
  tend to `lb` along `l`;
* `f.comp_tendsto g hg` and `f.comp_tendsto' g hg`: given `f : germ l β` and a function
  `g : γ → α` (resp., a germ `g : germ lc α`), if `g` tends to `l` along `lc`, then the composition
  `f ∘ g` is a well-defined germ at `lc`;
* `germ.lift_pred`, `germ.lift_rel`: lift a predicate or a relation to the space of germs:
  `(f : germ l β).lift_pred p` means `∀ᶠ x in l, p (f x)`, and similarly for a relation.
[TPiL_coe]: https://leanprover.github.io/theorem_proving_in_lean/type_classes.html#coercions-using-type-classes

We also define `map (F : β → γ) : germ l β → germ l γ` sending each germ `f` to `F ∘ f`.

For each of the following structures we prove that if `β` has this structure, then so does
`germ l β`:

* one-operation algebraic structures up to `comm_group`;
* `mul_zero_class`, `distrib`, `semiring`, `comm_semiring`, `ring`, `comm_ring`;
* `mul_action`, `distrib_mul_action`, `semimodule`;
* `preorder`, `partial_order`, and `lattice` structures up to `bounded_lattice`;
* `ordered_cancel_comm_monoid` and `ordered_cancel_add_comm_monoid`.

## Tags

filter, germ
-/

namespace filter

theorem const_eventually_eq' {α : Type u_1} {β : Type u_2} {l : filter α} [ne_bot l] {a : β} {b : β} : filter.eventually (fun (x : α) => a = b) l ↔ a = b :=
  eventually_const

theorem const_eventually_eq {α : Type u_1} {β : Type u_2} {l : filter α} [ne_bot l] {a : β} {b : β} : (eventually_eq l (fun (_x : α) => a) fun (_x : α) => b) ↔ a = b :=
  const_eventually_eq'

theorem eventually_eq.comp_tendsto {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} {f : α → β} {f' : α → β} (H : eventually_eq l f f') {g : γ → α} {lc : filter γ} (hg : tendsto g lc l) : eventually_eq lc (f ∘ g) (f' ∘ g) :=
  tendsto.eventually hg H

/-- Setoid used to define the space of germs. -/
def germ_setoid {α : Type u_1} (l : filter α) (β : Type u_2) : setoid (α → β) :=
  setoid.mk (eventually_eq l) sorry

/-- The space of germs of functions `α → β` at a filter `l`. -/
def germ {α : Type u_1} (l : filter α) (β : Type u_2) :=
  quotient (germ_setoid l β)

namespace germ

protected instance has_coe_t {α : Type u_1} {β : Type u_2} {l : filter α} : has_coe_t (α → β) (germ l β) :=
  has_coe_t.mk quotient.mk'

protected instance has_lift_t {α : Type u_1} {β : Type u_2} {l : filter α} : has_lift_t β (germ l β) :=
  has_lift_t.mk fun (c : β) => ↑fun (x : α) => c

@[simp] theorem quot_mk_eq_coe {α : Type u_1} {β : Type u_2} (l : filter α) (f : α → β) : Quot.mk setoid.r f = ↑f :=
  rfl

@[simp] theorem mk'_eq_coe {α : Type u_1} {β : Type u_2} (l : filter α) (f : α → β) : quotient.mk' f = ↑f :=
  rfl

theorem induction_on {α : Type u_1} {β : Type u_2} {l : filter α} (f : germ l β) {p : germ l β → Prop} (h : ∀ (f : α → β), p ↑f) : p f :=
  quotient.induction_on' f h

theorem induction_on₂ {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (f : germ l β) (g : germ l γ) {p : germ l β → germ l γ → Prop} (h : ∀ (f : α → β) (g : α → γ), p ↑f ↑g) : p f g :=
  quotient.induction_on₂' f g h

theorem induction_on₃ {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} {l : filter α} (f : germ l β) (g : germ l γ) (h : germ l δ) {p : germ l β → germ l γ → germ l δ → Prop} (H : ∀ (f : α → β) (g : α → γ) (h : α → δ), p ↑f ↑g ↑h) : p f g h :=
  quotient.induction_on₃' f g h H

/-- Given a map `F : (α → β) → (γ → δ)` that sends functions eventually equal at `l` to functions
eventually equal at `lc`, returns a map from `germ l β` to `germ lc δ`. -/
def map' {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} {l : filter α} {lc : filter γ} (F : (α → β) → γ → δ) (hF : relator.lift_fun (eventually_eq l) (eventually_eq lc) F F) : germ l β → germ lc δ :=
  quotient.map' F hF

/-- Given a germ `f : germ l β` and a function `F : (α → β) → γ` sending eventually equal functions
to the same value, returns the value `F` takes on functions having germ `f` at `l`. -/
def lift_on {α : Type u_1} {β : Type u_2} {l : filter α} {γ : Sort u_3} (f : germ l β) (F : (α → β) → γ) (hF : relator.lift_fun (eventually_eq l) Eq F F) : γ :=
  quotient.lift_on' f F hF

@[simp] theorem map'_coe {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} {l : filter α} {lc : filter γ} (F : (α → β) → γ → δ) (hF : relator.lift_fun (eventually_eq l) (eventually_eq lc) F F) (f : α → β) : map' F hF ↑f = ↑(F f) :=
  rfl

@[simp] theorem coe_eq {α : Type u_1} {β : Type u_2} {l : filter α} {f : α → β} {g : α → β} : ↑f = ↑g ↔ eventually_eq l f g :=
  quotient.eq'

theorem Mathlib.filter.eventually_eq.germ_eq {α : Type u_1} {β : Type u_2} {l : filter α} {f : α → β} {g : α → β} : eventually_eq l f g → ↑f = ↑g :=
  iff.mpr coe_eq

/-- Lift a function `β → γ` to a function `germ l β → germ l γ`. -/
def map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (op : β → γ) : germ l β → germ l γ :=
  map' (function.comp op) sorry

@[simp] theorem map_coe {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (op : β → γ) (f : α → β) : map op ↑f = ↑(op ∘ f) :=
  rfl

@[simp] theorem map_id {α : Type u_1} {β : Type u_2} {l : filter α} : map id = id :=
  funext fun (x : germ l β) => quot.induction_on x fun (f : α → β) => Eq.refl (map id (Quot.mk setoid.r f))

theorem map_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} {l : filter α} (op₁ : γ → δ) (op₂ : β → γ) (f : germ l β) : map op₁ (map op₂ f) = map (op₁ ∘ op₂) f :=
  induction_on f fun (f : α → β) => rfl

/-- Lift a binary function `β → γ → δ` to a function `germ l β → germ l γ → germ l δ`. -/
def map₂ {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} {l : filter α} (op : β → γ → δ) : germ l β → germ l γ → germ l δ :=
  quotient.map₂' (fun (f : α → β) (g : α → γ) (x : α) => op (f x) (g x)) sorry

@[simp] theorem map₂_coe {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} {l : filter α} (op : β → γ → δ) (f : α → β) (g : α → γ) : map₂ op ↑f ↑g = ↑fun (x : α) => op (f x) (g x) :=
  rfl

/-- A germ at `l` of maps from `α` to `β` tends to `lb : filter β` if it is represented by a map
which tends to `lb` along `l`. -/
protected def tendsto {α : Type u_1} {β : Type u_2} {l : filter α} (f : germ l β) (lb : filter β) :=
  lift_on f (fun (f : α → β) => tendsto f l lb) sorry

@[simp] theorem coe_tendsto {α : Type u_1} {β : Type u_2} {l : filter α} {f : α → β} {lb : filter β} : germ.tendsto (↑f) lb ↔ tendsto f l lb :=
  iff.rfl

theorem Mathlib.filter.tendsto.germ_tendsto {α : Type u_1} {β : Type u_2} {l : filter α} {f : α → β} {lb : filter β} : tendsto f l lb → germ.tendsto (↑f) lb :=
  iff.mpr coe_tendsto

/-- Given two germs `f : germ l β`, and `g : germ lc α`, where `l : filter α`, if `g` tends to `l`,
then the composition `f ∘ g` is well-defined as a germ at `lc`. -/
def comp_tendsto' {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (f : germ l β) {lc : filter γ} (g : germ lc α) (hg : germ.tendsto g l) : germ lc β :=
  lift_on f (fun (f : α → β) => map f g) sorry

@[simp] theorem coe_comp_tendsto' {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (f : α → β) {lc : filter γ} {g : germ lc α} (hg : germ.tendsto g l) : comp_tendsto' (↑f) g hg = map f g :=
  rfl

/-- Given a germ `f : germ l β` and a function `g : γ → α`, where `l : filter α`, if `g` tends
to `l` along `lc : filter γ`, then the composition `f ∘ g` is well-defined as a germ at `lc`. -/
def comp_tendsto {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (f : germ l β) {lc : filter γ} (g : γ → α) (hg : tendsto g lc l) : germ lc β :=
  comp_tendsto' f (↑g) (tendsto.germ_tendsto hg)

@[simp] theorem coe_comp_tendsto {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (f : α → β) {lc : filter γ} {g : γ → α} (hg : tendsto g lc l) : comp_tendsto (↑f) g hg = ↑(f ∘ g) :=
  rfl

@[simp] theorem comp_tendsto'_coe {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (f : germ l β) {lc : filter γ} {g : γ → α} (hg : tendsto g lc l) : comp_tendsto' f (↑g) (tendsto.germ_tendsto hg) = comp_tendsto f g hg :=
  rfl

@[simp] theorem const_inj {α : Type u_1} {β : Type u_2} {l : filter α} [ne_bot l] {a : β} {b : β} : ↑a = ↑b ↔ a = b :=
  iff.trans coe_eq const_eventually_eq

@[simp] theorem map_const {α : Type u_1} {β : Type u_2} {γ : Type u_3} (l : filter α) (a : β) (f : β → γ) : map f ↑a = ↑(f a) :=
  rfl

@[simp] theorem map₂_const {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} (l : filter α) (b : β) (c : γ) (f : β → γ → δ) : map₂ f ↑b ↑c = ↑(f b c) :=
  rfl

@[simp] theorem const_comp_tendsto {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (b : β) {lc : filter γ} {g : γ → α} (hg : tendsto g lc l) : comp_tendsto (↑b) g hg = ↑b :=
  rfl

@[simp] theorem const_comp_tendsto' {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (b : β) {lc : filter γ} {g : germ lc α} (hg : germ.tendsto g l) : comp_tendsto' (↑b) g hg = ↑b :=
  induction_on g (fun (_x : γ → α) (_x_1 : germ.tendsto (↑_x) l) => rfl) hg

/-- Lift a predicate on `β` to `germ l β`. -/
def lift_pred {α : Type u_1} {β : Type u_2} {l : filter α} (p : β → Prop) (f : germ l β) :=
  lift_on f (fun (f : α → β) => filter.eventually (fun (x : α) => p (f x)) l) sorry

@[simp] theorem lift_pred_coe {α : Type u_1} {β : Type u_2} {l : filter α} {p : β → Prop} {f : α → β} : lift_pred p ↑f ↔ filter.eventually (fun (x : α) => p (f x)) l :=
  iff.rfl

theorem lift_pred_const {α : Type u_1} {β : Type u_2} {l : filter α} {p : β → Prop} {x : β} (hx : p x) : lift_pred p ↑x :=
  eventually_of_forall fun (y : α) => hx

@[simp] theorem lift_pred_const_iff {α : Type u_1} {β : Type u_2} {l : filter α} [ne_bot l] {p : β → Prop} {x : β} : lift_pred p ↑x ↔ p x :=
  eventually_const

/-- Lift a relation `r : β → γ → Prop` to `germ l β → germ l γ → Prop`. -/
def lift_rel {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} (r : β → γ → Prop) (f : germ l β) (g : germ l γ) :=
  quotient.lift_on₂' f g (fun (f : α → β) (g : α → γ) => filter.eventually (fun (x : α) => r (f x) (g x)) l) sorry

@[simp] theorem lift_rel_coe {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} {r : β → γ → Prop} {f : α → β} {g : α → γ} : lift_rel r ↑f ↑g ↔ filter.eventually (fun (x : α) => r (f x) (g x)) l :=
  iff.rfl

theorem lift_rel_const {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} {r : β → γ → Prop} {x : β} {y : γ} (h : r x y) : lift_rel r ↑x ↑y :=
  eventually_of_forall fun (_x : α) => h

@[simp] theorem lift_rel_const_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} {l : filter α} [ne_bot l] {r : β → γ → Prop} {x : β} {y : γ} : lift_rel r ↑x ↑y ↔ r x y :=
  eventually_const

protected instance inhabited {α : Type u_1} {β : Type u_2} {l : filter α} [Inhabited β] : Inhabited (germ l β) :=
  { default := ↑Inhabited.default }

protected instance has_add {α : Type u_1} {l : filter α} {M : Type u_5} [Add M] : Add (germ l M) :=
  { add := map₂ Add.add }

@[simp] theorem coe_add {α : Type u_1} {l : filter α} {M : Type u_5} [Add M] (f : α → M) (g : α → M) : ↑(f + g) = ↑f + ↑g :=
  rfl

protected instance has_one {α : Type u_1} {l : filter α} {M : Type u_5} [HasOne M] : HasOne (germ l M) :=
  { one := ↑1 }

@[simp] theorem coe_zero {α : Type u_1} {l : filter α} {M : Type u_5} [HasZero M] : ↑0 = 0 :=
  rfl

protected instance add_semigroup {α : Type u_1} {l : filter α} {M : Type u_5} [add_semigroup M] : add_semigroup (germ l M) :=
  add_semigroup.mk Add.add sorry

protected instance add_comm_semigroup {α : Type u_1} {l : filter α} {M : Type u_5} [add_comm_semigroup M] : add_comm_semigroup (germ l M) :=
  add_comm_semigroup.mk Add.add sorry sorry

protected instance left_cancel_semigroup {α : Type u_1} {l : filter α} {M : Type u_5} [left_cancel_semigroup M] : left_cancel_semigroup (germ l M) :=
  left_cancel_semigroup.mk Mul.mul sorry sorry

protected instance right_cancel_semigroup {α : Type u_1} {l : filter α} {M : Type u_5} [right_cancel_semigroup M] : right_cancel_semigroup (germ l M) :=
  right_cancel_semigroup.mk Mul.mul sorry sorry

protected instance monoid {α : Type u_1} {l : filter α} {M : Type u_5} [monoid M] : monoid (germ l M) :=
  monoid.mk Mul.mul sorry 1 sorry sorry

/-- coercion from functions to germs as a monoid homomorphism. -/
def coe_mul_hom {α : Type u_1} {M : Type u_5} [monoid M] (l : filter α) : (α → M) →* germ l M :=
  monoid_hom.mk coe sorry sorry

/-- coercion from functions to germs as an additive monoid homomorphism. -/
@[simp] theorem coe_coe_mul_hom {α : Type u_1} {l : filter α} {M : Type u_5} [monoid M] : ⇑(coe_mul_hom l) = coe :=
  rfl

protected instance add_comm_monoid {α : Type u_1} {l : filter α} {M : Type u_5} [add_comm_monoid M] : add_comm_monoid (germ l M) :=
  add_comm_monoid.mk Add.add sorry 0 sorry sorry sorry

protected instance has_neg {α : Type u_1} {l : filter α} {G : Type u_6} [Neg G] : Neg (germ l G) :=
  { neg := map Neg.neg }

@[simp] theorem coe_neg {α : Type u_1} {l : filter α} {G : Type u_6} [Neg G] (f : α → G) : ↑(-f) = -↑f :=
  rfl

protected instance has_div {α : Type u_1} {l : filter α} {M : Type u_5} [Div M] : Div (germ l M) :=
  { div := map₂ Div.div }

@[simp] theorem coe_div {α : Type u_1} {l : filter α} {M : Type u_5} [Div M] (f : α → M) (g : α → M) : ↑(f / g) = ↑f / ↑g :=
  rfl

protected instance sub_neg_add_monoid {α : Type u_1} {l : filter α} {G : Type u_6} [sub_neg_monoid G] : sub_neg_monoid (germ l G) :=
  sub_neg_monoid.mk add_monoid.add sorry add_monoid.zero sorry sorry Neg.neg Sub.sub

protected instance group {α : Type u_1} {l : filter α} {G : Type u_6} [group G] : group (germ l G) :=
  group.mk Mul.mul sorry 1 sorry sorry div_inv_monoid.inv div_inv_monoid.div sorry

protected instance comm_group {α : Type u_1} {l : filter α} {G : Type u_6} [comm_group G] : comm_group (germ l G) :=
  comm_group.mk Mul.mul sorry 1 sorry sorry has_inv.inv group.div sorry sorry

protected instance nontrivial {α : Type u_1} {l : filter α} {R : Type u_5} [nontrivial R] [ne_bot l] : nontrivial (germ l R) :=
  sorry

protected instance mul_zero_class {α : Type u_1} {l : filter α} {R : Type u_5} [mul_zero_class R] : mul_zero_class (germ l R) :=
  mul_zero_class.mk Mul.mul 0 sorry sorry

protected instance distrib {α : Type u_1} {l : filter α} {R : Type u_5} [distrib R] : distrib (germ l R) :=
  distrib.mk Mul.mul Add.add sorry sorry

protected instance semiring {α : Type u_1} {l : filter α} {R : Type u_5} [semiring R] : semiring (germ l R) :=
  semiring.mk add_comm_monoid.add sorry add_comm_monoid.zero sorry sorry sorry monoid.mul sorry monoid.one sorry sorry
    sorry sorry sorry sorry

/-- Coercion `(α → R) → germ l R` as a `ring_hom`. -/
def coe_ring_hom {α : Type u_1} {R : Type u_5} [semiring R] (l : filter α) : (α → R) →+* germ l R :=
  ring_hom.mk coe sorry sorry sorry sorry

@[simp] theorem coe_coe_ring_hom {α : Type u_1} {l : filter α} {R : Type u_5} [semiring R] : ⇑(coe_ring_hom l) = coe :=
  rfl

protected instance ring {α : Type u_1} {l : filter α} {R : Type u_5} [ring R] : ring (germ l R) :=
  ring.mk add_comm_group.add sorry add_comm_group.zero sorry sorry add_comm_group.neg add_comm_group.sub sorry sorry
    monoid.mul sorry monoid.one sorry sorry sorry sorry

protected instance comm_semiring {α : Type u_1} {l : filter α} {R : Type u_5} [comm_semiring R] : comm_semiring (germ l R) :=
  comm_semiring.mk semiring.add sorry semiring.zero sorry sorry sorry semiring.mul sorry semiring.one sorry sorry sorry
    sorry sorry sorry sorry

protected instance comm_ring {α : Type u_1} {l : filter α} {R : Type u_5} [comm_ring R] : comm_ring (germ l R) :=
  comm_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry ring.one sorry sorry
    sorry sorry sorry

protected instance has_scalar {α : Type u_1} {β : Type u_2} {l : filter α} {M : Type u_5} [has_scalar M β] : has_scalar M (germ l β) :=
  has_scalar.mk fun (c : M) => map (has_scalar.smul c)

protected instance has_scalar' {α : Type u_1} {β : Type u_2} {l : filter α} {M : Type u_5} [has_scalar M β] : has_scalar (germ l M) (germ l β) :=
  has_scalar.mk (map₂ has_scalar.smul)

@[simp] theorem coe_smul {α : Type u_1} {β : Type u_2} {l : filter α} {M : Type u_5} [has_scalar M β] (c : M) (f : α → β) : ↑(c • f) = c • ↑f :=
  rfl

@[simp] theorem coe_smul' {α : Type u_1} {β : Type u_2} {l : filter α} {M : Type u_5} [has_scalar M β] (c : α → M) (f : α → β) : ↑(c • f) = ↑c • ↑f :=
  rfl

protected instance mul_action {α : Type u_1} {β : Type u_2} {l : filter α} {M : Type u_5} [monoid M] [mul_action M β] : mul_action M (germ l β) :=
  mul_action.mk sorry sorry

protected instance mul_action' {α : Type u_1} {β : Type u_2} {l : filter α} {M : Type u_5} [monoid M] [mul_action M β] : mul_action (germ l M) (germ l β) :=
  mul_action.mk sorry sorry

protected instance distrib_mul_action {α : Type u_1} {l : filter α} {M : Type u_5} {N : Type u_6} [monoid M] [add_monoid N] [distrib_mul_action M N] : distrib_mul_action M (germ l N) :=
  distrib_mul_action.mk sorry sorry

protected instance distrib_mul_action' {α : Type u_1} {l : filter α} {M : Type u_5} {N : Type u_6} [monoid M] [add_monoid N] [distrib_mul_action M N] : distrib_mul_action (germ l M) (germ l N) :=
  distrib_mul_action.mk sorry sorry

protected instance semimodule {α : Type u_1} {l : filter α} {M : Type u_5} {R : Type u_7} [semiring R] [add_comm_monoid M] [semimodule R M] : semimodule R (germ l M) :=
  semimodule.mk sorry sorry

protected instance semimodule' {α : Type u_1} {l : filter α} {M : Type u_5} {R : Type u_7} [semiring R] [add_comm_monoid M] [semimodule R M] : semimodule (germ l R) (germ l M) :=
  semimodule.mk sorry sorry

protected instance has_le {α : Type u_1} {β : Type u_2} {l : filter α} [HasLessEq β] : HasLessEq (germ l β) :=
  { LessEq := lift_rel LessEq }

@[simp] theorem coe_le {α : Type u_1} {β : Type u_2} {l : filter α} {f : α → β} {g : α → β} [HasLessEq β] : ↑f ≤ ↑g ↔ eventually_le l f g :=
  iff.rfl

theorem le_def {α : Type u_1} {β : Type u_2} {l : filter α} [HasLessEq β] : LessEq = lift_rel LessEq :=
  rfl

theorem const_le {α : Type u_1} {β : Type u_2} {l : filter α} [HasLessEq β] {x : β} {y : β} (h : x ≤ y) : ↑x ≤ ↑y :=
  lift_rel_const h

@[simp] theorem const_le_iff {α : Type u_1} {β : Type u_2} {l : filter α} [HasLessEq β] [ne_bot l] {x : β} {y : β} : ↑x ≤ ↑y ↔ x ≤ y :=
  lift_rel_const_iff

protected instance preorder {α : Type u_1} {β : Type u_2} {l : filter α} [preorder β] : preorder (germ l β) :=
  preorder.mk LessEq (fun (a b : germ l β) => a ≤ b ∧ ¬b ≤ a) sorry sorry

protected instance partial_order {α : Type u_1} {β : Type u_2} {l : filter α} [partial_order β] : partial_order (germ l β) :=
  partial_order.mk LessEq preorder.lt sorry sorry sorry

protected instance has_bot {α : Type u_1} {β : Type u_2} {l : filter α} [has_bot β] : has_bot (germ l β) :=
  has_bot.mk ↑⊥

@[simp] theorem const_bot {α : Type u_1} {β : Type u_2} {l : filter α} [has_bot β] : ↑⊥ = ⊥ :=
  rfl

protected instance order_bot {α : Type u_1} {β : Type u_2} {l : filter α} [order_bot β] : order_bot (germ l β) :=
  order_bot.mk ⊥ LessEq partial_order.lt sorry sorry sorry sorry

protected instance has_top {α : Type u_1} {β : Type u_2} {l : filter α} [has_top β] : has_top (germ l β) :=
  has_top.mk ↑⊤

@[simp] theorem const_top {α : Type u_1} {β : Type u_2} {l : filter α} [has_top β] : ↑⊤ = ⊤ :=
  rfl

protected instance order_top {α : Type u_1} {β : Type u_2} {l : filter α} [order_top β] : order_top (germ l β) :=
  order_top.mk ⊤ LessEq partial_order.lt sorry sorry sorry sorry

protected instance has_sup {α : Type u_1} {β : Type u_2} {l : filter α} [has_sup β] : has_sup (germ l β) :=
  has_sup.mk (map₂ has_sup.sup)

@[simp] theorem const_sup {α : Type u_1} {β : Type u_2} {l : filter α} [has_sup β] (a : β) (b : β) : ↑(a ⊔ b) = ↑a ⊔ ↑b :=
  rfl

protected instance has_inf {α : Type u_1} {β : Type u_2} {l : filter α} [has_inf β] : has_inf (germ l β) :=
  has_inf.mk (map₂ has_inf.inf)

@[simp] theorem const_inf {α : Type u_1} {β : Type u_2} {l : filter α} [has_inf β] (a : β) (b : β) : ↑(a ⊓ b) = ↑a ⊓ ↑b :=
  rfl

protected instance semilattice_sup {α : Type u_1} {β : Type u_2} {l : filter α} [semilattice_sup β] : semilattice_sup (germ l β) :=
  semilattice_sup.mk has_sup.sup partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry

protected instance semilattice_inf {α : Type u_1} {β : Type u_2} {l : filter α} [semilattice_inf β] : semilattice_inf (germ l β) :=
  semilattice_inf.mk has_inf.inf partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry

protected instance semilattice_inf_bot {α : Type u_1} {β : Type u_2} {l : filter α} [semilattice_inf_bot β] : semilattice_inf_bot (germ l β) :=
  semilattice_inf_bot.mk order_bot.bot semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

protected instance semilattice_sup_bot {α : Type u_1} {β : Type u_2} {l : filter α} [semilattice_sup_bot β] : semilattice_sup_bot (germ l β) :=
  semilattice_sup_bot.mk order_bot.bot semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance semilattice_inf_top {α : Type u_1} {β : Type u_2} {l : filter α} [semilattice_inf_top β] : semilattice_inf_top (germ l β) :=
  semilattice_inf_top.mk order_top.top semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

protected instance semilattice_sup_top {α : Type u_1} {β : Type u_2} {l : filter α} [semilattice_sup_top β] : semilattice_sup_top (germ l β) :=
  semilattice_sup_top.mk order_top.top semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance lattice {α : Type u_1} {β : Type u_2} {l : filter α} [lattice β] : lattice (germ l β) :=
  lattice.mk semilattice_sup.sup semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry sorry sorry
    semilattice_inf.inf sorry sorry sorry

protected instance bounded_lattice {α : Type u_1} {β : Type u_2} {l : filter α} [bounded_lattice β] : bounded_lattice (germ l β) :=
  bounded_lattice.mk lattice.sup lattice.le lattice.lt sorry sorry sorry sorry sorry sorry lattice.inf sorry sorry sorry
    order_top.top sorry order_bot.bot sorry

protected instance ordered_cancel_add_comm_monoid {α : Type u_1} {β : Type u_2} {l : filter α} [ordered_cancel_add_comm_monoid β] : ordered_cancel_add_comm_monoid (germ l β) :=
  ordered_cancel_add_comm_monoid.mk add_comm_monoid.add sorry sorry add_comm_monoid.zero sorry sorry sorry sorry
    partial_order.le partial_order.lt sorry sorry sorry sorry sorry

protected instance ordered_add_comm_group {α : Type u_1} {β : Type u_2} {l : filter α} [ordered_add_comm_group β] : ordered_add_comm_group (germ l β) :=
  ordered_add_comm_group.mk add_comm_group.add sorry add_comm_group.zero sorry sorry add_comm_group.neg add_comm_group.sub
    sorry sorry partial_order.le partial_order.lt sorry sorry sorry sorry


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

! This file was ported from Lean 3 source module algebraic_geometry.morphisms.finite_type
! leanprover-community/mathlib commit 70fd9563a21e7b963887c9360bd29b2393e6225a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.AlgebraicGeometry.Morphisms.RingHomProperties
import Mathbin.RingTheory.RingHom.FiniteType

/-!
# Morphisms of finite type

A morphism of schemes `f : X ⟶ Y` is locally of finite type if for each affine `U ⊆ Y` and
`V ⊆ f ⁻¹' U`, The induced map `Γ(Y, U) ⟶ Γ(X, V)` is of finite type.

A morphism of schemes is of finite type if it is both locally of finite type and quasi-compact.

We show that these properties are local, and are stable under compositions.

-/

noncomputable section

open CategoryTheory CategoryTheory.Limits Opposite TopologicalSpace

universe v u

namespace AlgebraicGeometry

variable {X Y : Scheme.{u}} (f : X ⟶ Y)

/-- A morphism of schemes `f : X ⟶ Y` is locally of finite type if for each affine `U ⊆ Y` and
`V ⊆ f ⁻¹' U`, The induced map `Γ(Y, U) ⟶ Γ(X, V)` is of finite type.
-/
@[mk_iff]
class LocallyOfFiniteType (f : X ⟶ Y) : Prop where
  finiteType_of_affine_subset :
    ∀ (U : Y.affineOpens) (V : X.affineOpens) (e : V.1 ≤ (Opens.map f.1.base).obj U.1),
      (f.appLe e).FiniteType
#align algebraic_geometry.locally_of_finite_type AlgebraicGeometry.LocallyOfFiniteType

theorem locallyOfFiniteType_eq : @LocallyOfFiniteType = affineLocally @RingHom.FiniteType :=
  by
  ext (X Y f)
  rw [locally_of_finite_type_iff, affine_locally_iff_affine_opens_le]
  exact RingHom.finiteType_respectsIso
#align algebraic_geometry.locally_of_finite_type_eq AlgebraicGeometry.locallyOfFiniteType_eq

instance (priority := 900) locallyOfFiniteTypeOfIsOpenImmersion {X Y : Scheme} (f : X ⟶ Y)
    [IsOpenImmersion f] : LocallyOfFiniteType f :=
  locallyOfFiniteType_eq.symm ▸ RingHom.finiteType_is_local.affineLocally_of_isOpenImmersion f
#align algebraic_geometry.locally_of_finite_type_of_is_open_immersion AlgebraicGeometry.locallyOfFiniteTypeOfIsOpenImmersion

theorem locallyOfFiniteType_stableUnderComposition :
    MorphismProperty.StableUnderComposition @LocallyOfFiniteType :=
  locallyOfFiniteType_eq.symm ▸ RingHom.finiteType_is_local.affineLocally_stableUnderComposition
#align algebraic_geometry.locally_of_finite_type_stable_under_composition AlgebraicGeometry.locallyOfFiniteType_stableUnderComposition

instance locallyOfFiniteTypeComp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
    [hf : LocallyOfFiniteType f] [hg : LocallyOfFiniteType g] : LocallyOfFiniteType (f ≫ g) :=
  locallyOfFiniteType_stableUnderComposition f g hf hg
#align algebraic_geometry.locally_of_finite_type_comp AlgebraicGeometry.locallyOfFiniteTypeComp

theorem locallyOfFiniteTypeOfComp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
    [hf : LocallyOfFiniteType (f ≫ g)] : LocallyOfFiniteType f :=
  by
  revert hf
  rw [locally_of_finite_type_eq]
  apply ring_hom.finite_type_is_local.affine_locally_of_comp
  introv H
  exact RingHom.FiniteType.of_comp_finiteType H
#align algebraic_geometry.locally_of_finite_type_of_comp AlgebraicGeometry.locallyOfFiniteTypeOfComp

theorem LocallyOfFiniteType.affine_openCover_iff {X Y : Scheme.{u}} (f : X ⟶ Y)
    (𝒰 : Scheme.OpenCover.{u} Y) [∀ i, IsAffine (𝒰.obj i)]
    (𝒰' : ∀ i, Scheme.OpenCover.{u} ((𝒰.pullbackCover f).obj i)) [∀ i j, IsAffine ((𝒰' i).obj j)] :
    LocallyOfFiniteType f ↔ ∀ i j, (Scheme.Γ.map ((𝒰' i).map j ≫ pullback.snd).op).FiniteType :=
  locallyOfFiniteType_eq.symm ▸ RingHom.finiteType_is_local.affine_openCover_iff f 𝒰 𝒰'
#align algebraic_geometry.locally_of_finite_type.affine_open_cover_iff AlgebraicGeometry.LocallyOfFiniteType.affine_openCover_iff

theorem LocallyOfFiniteType.source_openCover_iff {X Y : Scheme.{u}} (f : X ⟶ Y)
    (𝒰 : Scheme.OpenCover.{u} X) : LocallyOfFiniteType f ↔ ∀ i, LocallyOfFiniteType (𝒰.map i ≫ f) :=
  locallyOfFiniteType_eq.symm ▸ RingHom.finiteType_is_local.source_openCover_iff f 𝒰
#align algebraic_geometry.locally_of_finite_type.source_open_cover_iff AlgebraicGeometry.LocallyOfFiniteType.source_openCover_iff

theorem LocallyOfFiniteType.openCover_iff {X Y : Scheme.{u}} (f : X ⟶ Y)
    (𝒰 : Scheme.OpenCover.{u} Y) :
    LocallyOfFiniteType f ↔ ∀ i, LocallyOfFiniteType (pullback.snd : pullback f (𝒰.map i) ⟶ _) :=
  locallyOfFiniteType_eq.symm ▸ RingHom.finiteType_is_local.isLocalAffineLocally.openCover_iff f 𝒰
#align algebraic_geometry.locally_of_finite_type.open_cover_iff AlgebraicGeometry.LocallyOfFiniteType.openCover_iff

theorem locallyOfFiniteType_respectsIso : MorphismProperty.RespectsIso @LocallyOfFiniteType :=
  locallyOfFiniteType_eq.symm ▸
    targetAffineLocally_respectsIso (sourceAffineLocally_respectsIso RingHom.finiteType_respectsIso)
#align algebraic_geometry.locally_of_finite_type_respects_iso AlgebraicGeometry.locallyOfFiniteType_respectsIso

end AlgebraicGeometry


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

import algebra.module.pi
import data.polynomial.basic
import group_theory.group_action.prod
import group_theory.group_action.units
import data.complex.module

/-! # Tests that instances do not form diamonds -/

/-! ## Scalar action instances -/
section has_scalar

example :
  (sub_neg_monoid.has_scalar_int : has_scalar ℤ ℂ) = (complex.has_scalar : has_scalar ℤ ℂ) :=
rfl

example (α β : Type*) [add_monoid α] [add_monoid β] :
  (prod.has_scalar : has_scalar ℕ (α × β)) = add_monoid.has_scalar_nat := rfl

example (α β : Type*) [sub_neg_monoid α] [sub_neg_monoid β] :
  (prod.has_scalar : has_scalar ℤ (α × β)) = sub_neg_monoid.has_scalar_int := rfl

example (α : Type*) (β : α → Type*) [Π a, add_monoid (β a)] :
  (pi.has_scalar : has_scalar ℕ (Π a, β a)) = add_monoid.has_scalar_nat := rfl

example (α : Type*) (β : α → Type*) [Π a, sub_neg_monoid (β a)] :
  (pi.has_scalar : has_scalar ℤ (Π a, β a)) = sub_neg_monoid.has_scalar_int := rfl

section units

example (α : Type*) [monoid α] :
  (units.mul_action : mul_action (units α) (α × α)) = prod.mul_action := rfl

example (R α : Type*) (β : α → Type*) [monoid R] [Π i, mul_action R (β i)] :
  (units.mul_action : mul_action (units R) (Π i, β i)) = pi.mul_action _ := rfl

example (R α : Type*) (β : α → Type*) [monoid R] [semiring α] [distrib_mul_action R α] :
  (units.distrib_mul_action : distrib_mul_action (units R) (polynomial α)) =
    polynomial.distrib_mul_action :=
rfl

/-!
TODO: https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/units.2Emul_action'.20diamond/near/246402813
```lean
example {α : Type*} [comm_monoid α] :
  (units.mul_action' : mul_action (units α) (units α)) = monoid.to_mul_action _ :=
rfl -- fails
```
-/

end units

end has_scalar

/-! ## `with_top` (Type with point at infinity) instances -/
section with_top

example (R : Type*) [h : ordered_semiring R] :
  (@with_top.add_comm_monoid R
    (@non_unital_non_assoc_semiring.to_add_comm_monoid R
      (@non_assoc_semiring.to_non_unital_non_assoc_semiring R
        (@semiring.to_non_assoc_semiring R
          (@ordered_semiring.to_semiring R h)))))
        =
  (@ordered_add_comm_monoid.to_add_comm_monoid (with_top R)
    (@with_top.ordered_add_comm_monoid R
      (@ordered_cancel_add_comm_monoid.to_ordered_add_comm_monoid R
        (@ordered_semiring.to_ordered_cancel_add_comm_monoid R h)))) :=
rfl

end with_top


import tactic.localized
import algebra.group_power.lemmas

open tactic
local infix ` ⊹ `:59 := nat.mul
local infix ` ↓ `:59 := pow
local infix ` ⊖ `:59 := pow
example : 2 ⊹ 3 = 6 := rfl
example : 2 ↓ 3 = 8 := rfl
example : 2 ⊖ 3 = 8 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { success_if_fail { simp [h] }, exact le_of_lt h }
section
localized "infix (name := plus) ` ⊹ `:59 := nat.add" in nat
localized "infix (name := down) ` ↓ `:59 := nat.mul" in nat
localized "infix (name := minus) ` ⊖ `:59 := nat.mul" in nat.mul
localized "attribute [simp] le_of_lt" in le
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 6 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { simp [h] }
end

section
example : 2 ⊹ 3 = 6 := rfl
example : 2 ↓ 3 = 8 := rfl
example : 2 ⊖ 3 = 8 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { success_if_fail { simp [h] }, exact le_of_lt h }

-- test that `open_locale` will fail when given a nonexistent locale
run_cmd success_if_fail $ get_localized [`ceci_nest_pas_une_locale]

open_locale nat
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 8 := rfl

open_locale nat.mul
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 6 := rfl
end

section
open_locale nat.mul nat nat.mul le
example : 2 ⊹ 3 = 5 := rfl
example : 2 ↓ 3 = 6 := rfl
example : 2 ⊖ 3 = 6 := rfl
example {n m : ℕ} (h : n < m) : n ≤ m := by { simp [h] }
end


import data.nat.basic
import tactic.basic

def foo : char → ℕ
| 'A' := 1
| 'B' := 2
| _   := 3

#print foo._main

inductive data
| const (n : ℕ) : data
| var (n : ℕ) : data
| plus s t : data
| times s t : data

open data

def eval (v : list ℕ): data → option ℕ
| (const n) := n
| (var n) := v.nth n
| (plus s t) := (eval s).bind (λ s, (eval t).map (λ t, s + t))
| (times s t) := (eval s).bind (λ s, (eval t).map (λ t, s * t))

#reduce eval [] (plus (const 1) (const 2))
#reduce eval [2,3,5] (times (var 0) (plus (var 1) (var 2)))

variable {α : Type}

/- this and the next makes simp way more powerful -/
local attribute [simp]
def len : list α → ℕ
| (x::xs) := 1 + len xs
| _ := 0

local attribute [simp]
def rev : list α → list α
| (x::xs) := rev xs ++ [x]
| _ := []

@[simp]
def rev' : list α → list α → list α
| [] r := r
| (x::xs) r := rev' xs (x::r)

@[simp]
def rev_tl (as : list α) : list α := rev' as []

-- @[simp]
-- lemma rev'_cons (a : α) (as : list α) (xs: list α):
--   rev' (a :: as) xs = rev' as (a::xs) := by induction as; simp

lemma rev'_app : ∀ as rs xs : list α,
  rev' as rs ++ xs = rev' as (rs ++ xs)
| [] _ _ := by simp
| _ _ [] := by simp
| (a::as) rs (x::xs) := by simp *

lemma rev_rev' (as : list α) : rev as = rev' as [] :=
begin
  induction as,
    simp *,
  unfold rev,
  unfold rev',
  rw as_ih,
  rw rev'_app,
  rw list.nil_append,
end

theorem tl_same (as : list α) : rev as = rev_tl as :=
by induction as; simp [rev_rev']

#reduce rev [1,2,3]

local attribute [simp]
theorem len_cons (x : α) (xs : list α) :
  len (x::xs) = 1 + len xs := by simp

local attribute [simp]
theorem len_app (s : list α) (t : list α) :
  len (s ++ t) = len s + len t := by induction s; simp [*, add_assoc]

theorem len_rev : ∀ as : list α, len (rev as) = len as
| (a::as) := by simp [*, add_comm]
| [] := rfl

theorem len_rev' (as : list α) : len (rev as) = len as :=
  by induction as; simp [*, add_comm]

open nat

theorem zero_add' : ∀ n, zero + n = n
| zero     := rfl
| (succ n) := congr_arg succ (zero_add' n)

#print acc

def nat_to_bin : ℕ → list ℕ
| 0       := [0]
| 1       := [1]
| (n + 2) :=
  have (n + 2) / 2 < n + 2, from sorry,
  nat_to_bin ((n + 2) / 2) ++ [n % 2]

#eval nat_to_bin 1234567

universe u

inductive vector (α : Type u) : nat → Type u
| nil {} : vector 0
| cons   : Π {n}, α → vector n → vector (n+1)

namespace vector
local notation h :: t := cons h t

#check @vector.cases_on
-- Π {α : Type}
--   {C : Π (a : ℕ), vector α a → Type}
--   {a : ℕ}
--   (n : vector α a),
--   (e1 : C 0 nil)
--   (e2 : Π {n : ℕ} (a : α) (a_1 : vector α n),
--           C (n + 1) (cons a a_1)),
--   C a n

local notation h :: t := cons h t

def tail_aux {α : Type} {n m : ℕ} (v : vector α m) :
    m = n + 1 → vector α n :=
vector.cases_on v
  (assume H : 0 = n + 1, nat.no_confusion H)
  (assume m (a : α) w : vector α m,
    assume H : m + 1 = n + 1,
      nat.no_confusion H (λ H1 : m = n, eq.rec_on H1 w))

def tail' {α : Type} {n : ℕ} (v : vector α (n+1)) :
  vector α n :=
tail_aux v rfl

def head {α : Type} : Π {n}, vector α (n+1) → α
| n (h :: t) := h

def tail {α : Type} : Π {n}, vector α (n+1) → vector α n
| n (h :: t) := t

lemma eta {α : Type} :
  ∀ {n} (v : vector α (n+1)), head v :: tail v = v
| n (h :: t) := rfl

def map {α β γ : Type} (f : α → β → γ) :
  Π {n}, vector α n → vector β n → vector γ n
| 0     nil       nil       := nil
| (n+1) (a :: va) (b :: vb) := f a b :: map va vb

#print map
#print map._main

def zip {α β : Type} :
  Π {n}, vector α n → vector β n → vector (α × β) n
| 0     nil       nil       := nil
| (n+1) (a :: va) (b :: vb) := (a, b) :: zip va vb

end vector

variable p : α → bool

def is_not_zero (m : ℕ) : bool :=
match m with
| 0     := ff
| (n+1) := tt
end

def filter : list α → list α
| []       := []
| (a :: l) :=
  match p a with
  |  tt := a :: filter l
  |  ff := filter l
  end

example : filter is_not_zero [1, 0, 0, 3, 0] = [1, 3] := rfl

def bar₁ : ℕ × ℕ → ℕ
| (m, n) := m + n

def bar₂ (p : ℕ × ℕ) : ℕ :=
match p with (m, n) := m + n end

def bar₃ : ℕ × ℕ → ℕ :=
λ ⟨m, n⟩, m + n

def bar₄ (p : ℕ × ℕ) : ℕ :=
let ⟨m, n⟩ := p in m + n


theorem test1 :∀ y:ℕ, ∃ x:ℕ, x-2=y:=λ y,exists.intro (y+2) rfl
#print classical.some
noncomputable def test2 : ℕ → ℕ :=λ y, classical.some (test1 y)
theorem test3 : ∀ y:ℕ,test2 y-2=y:=λ y,classical.some_spec (test1 y)
Sort hello
#check test3 7


import .var

-- some arithmetic variables
def X : var nat := var.mk 0
def Y : var nat := var.mk 1
def Z : var nat := var.mk 2

-- some Boolean variables
def P : var bool := var.mk 0
def Q : var bool := var.mk 1
def R : var bool := var.mk 2

-- test our var_eq function
example : var_eq X X = tt := rfl
example : var_eq X Y = ff := rfl


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import algebra.group.hom
import data.equiv.basic
/-!
# Type tags that turn additive structures into multiplicative, and vice versa

We define two type tags:

* `additive α`: turns any multiplicative structure on `α` into the corresponding
  additive structure on `additive α`;
* `multiplicative α`: turns any additive structure on `α` into the corresponding
  multiplicative structure on `multiplicative α`.

We also define instances `additive.*` and `multiplicative.*` that actually transfer the structures.
-/

universes u v
variables {α : Type u} {β : Type v}

/-- If `α` carries some multiplicative structure, then `additive α` carries the corresponding
additive structure. -/
def additive (α : Type*) := α
/-- If `α` carries some additive structure, then `multiplicative α` carries the corresponding
multiplicative structure. -/
def multiplicative (α : Type*) := α

namespace additive

/-- Reinterpret `x : α` as an element of `additive α`. -/
def of_mul : α ≃ additive α := ⟨λ x, x, λ x, x, λ x, rfl, λ x, rfl⟩

/-- Reinterpret `x : additive α` as an element of `α`. -/
def to_mul : additive α ≃ α := of_mul.symm

@[simp] lemma of_mul_symm_eq : (@of_mul α).symm = to_mul := rfl

@[simp] lemma to_mul_symm_eq : (@to_mul α).symm = of_mul := rfl

end additive

namespace multiplicative

/-- Reinterpret `x : α` as an element of `multiplicative α`. -/
def of_add : α ≃ multiplicative α := ⟨λ x, x, λ x, x, λ x, rfl, λ x, rfl⟩

/-- Reinterpret `x : multiplicative α` as an element of `α`. -/
def to_add : multiplicative α ≃ α := of_add.symm

@[simp] lemma of_add_symm_eq : (@of_add α).symm = to_add := rfl

@[simp]

end multiplicative

@[simp] lemma to_add_of_add (x : α) : (multiplicative.of_add x).to_add = x := rfl
@[simp] lemma of_add_to_add (x : multiplicative α) : multiplicative.of_add x.to_add = x := rfl

@[simp] lemma to_mul_of_mul (x : α) : (additive.of_mul x).to_mul = x := rfl
@[simp] lemma of_mul_to_mul (x : additive α) : additive.of_mul x.to_mul = x := rfl

instance [inhabited α] : inhabited (additive α) := ⟨additive.of_mul (default α)⟩
instance [inhabited α] : inhabited (multiplicative α) := ⟨multiplicative.of_add (default α)⟩

instance [nontrivial α] : nontrivial (additive α) :=
additive.of_mul.injective.nontrivial

instance [nontrivial α] : nontrivial (multiplicative α) :=
multiplicative.of_add.injective.nontrivial

instance additive.has_add [has_mul α] : has_add (additive α) :=
{ add := λ x y, additive.of_mul (x.to_mul * y.to_mul) }

instance [has_add α] : has_mul (multiplicative α) :=
{ mul := λ x y, multiplicative.of_add (x.to_add + y.to_add) }

@[simp] lemma of_add_add [has_add α] (x y : α) :
  multiplicative.of_add (x + y) = multiplicative.of_add x * multiplicative.of_add y :=
rfl

@[simp] lemma to_add_mul [has_add α] (x y : multiplicative α) :
  (x * y).to_add = x.to_add + y.to_add :=
rfl

@[simp] lemma of_mul_mul [has_mul α] (x y : α) :
  additive.of_mul (x * y) = additive.of_mul x + additive.of_mul y :=
rfl

@[simp] lemma to_mul_add [has_mul α] (x y : additive α) :
  (x + y).to_mul = x.to_mul * y.to_mul :=
rfl

instance [semigroup α] : add_semigroup (additive α) :=
{ add_assoc := @mul_assoc α _,
  ..additive.has_add }

instance [add_semigroup α] : semigroup (multiplicative α) :=
{ mul_assoc := @add_assoc α _,
  ..multiplicative.has_mul }

instance [comm_semigroup α] : add_comm_semigroup (additive α) :=
{ add_comm := @mul_comm _ _,
  ..additive.add_semigroup }

instance [add_comm_semigroup α] : comm_semigroup (multiplicative α) :=
{ mul_comm := @add_comm _ _,
  ..multiplicative.semigroup }

instance [left_cancel_semigroup α] : add_left_cancel_semigroup (additive α) :=
{ add_left_cancel := @mul_left_cancel _ _,
  ..additive.add_semigroup }

instance [add_left_cancel_semigroup α] : left_cancel_semigroup (multiplicative α) :=
{ mul_left_cancel := @add_left_cancel _ _,
  ..multiplicative.semigroup }

instance [right_cancel_semigroup α] : add_right_cancel_semigroup (additive α) :=
{ add_right_cancel := @mul_right_cancel _ _,
  ..additive.add_semigroup }

instance [add_right_cancel_semigroup α] : right_cancel_semigroup (multiplicative α) :=
{ mul_right_cancel := @add_right_cancel _ _,
  ..multiplicative.semigroup }

instance [has_one α] : has_zero (additive α) := ⟨additive.of_mul 1⟩

@[simp] lemma of_mul_one [has_one α] : @additive.of_mul α 1 = 0 := rfl

@[simp] lemma of_mul_eq_zero {A : Type*} [has_one A] {x : A} :
  additive.of_mul x = 0 ↔ x = 1 := iff.rfl

@[simp] lemma to_mul_zero [has_one α] : (0 : additive α).to_mul = 1 := rfl

instance [has_zero α] : has_one (multiplicative α) := ⟨multiplicative.of_add 0⟩

@[simp] lemma of_add_zero [has_zero α] : @multiplicative.of_add α 0 = 1 := rfl

@[simp] lemma of_add_eq_one {A : Type*} [has_zero A] {x : A} :
  multiplicative.of_add x = 1 ↔ x = 0 := iff.rfl

@[simp] lemma to_add_one [has_zero α] : (1 : multiplicative α).to_add = 0 := rfl

instance [mul_one_class α] : add_zero_class (additive α) :=
{ zero     := 0,
  add      := (+),
  zero_add := one_mul,
  add_zero := mul_one }

instance [add_zero_class α] : mul_one_class (multiplicative α) :=
{ one     := 1,
  mul     := (*),
  one_mul := zero_add,
  mul_one := add_zero }

instance [h : monoid α] : add_monoid (additive α) :=
{ zero     := 0,
  add      := (+),
  nsmul    := @npow α h,
  nsmul_zero' := monoid.npow_zero',
  nsmul_succ' := monoid.npow_succ',
  ..additive.add_zero_class,
  ..additive.add_semigroup }

instance [h : add_monoid α] : monoid (multiplicative α) :=
{ one     := 1,
  mul     := (*),
  npow   := @nsmul α h,
  npow_zero' := add_monoid.nsmul_zero',
  npow_succ' := add_monoid.nsmul_succ',
  ..multiplicative.mul_one_class,
  ..multiplicative.semigroup }

instance [left_cancel_monoid α] : add_left_cancel_monoid (additive α) :=
{ .. additive.add_monoid, .. additive.add_left_cancel_semigroup }

instance [add_left_cancel_monoid α] : left_cancel_monoid (multiplicative α) :=
{ .. multiplicative.monoid, .. multiplicative.left_cancel_semigroup }

instance [right_cancel_monoid α] : add_right_cancel_monoid (additive α) :=
{ .. additive.add_monoid, .. additive.add_right_cancel_semigroup }

instance [add_right_cancel_monoid α] : right_cancel_monoid (multiplicative α) :=
{ .. multiplicative.monoid, .. multiplicative.right_cancel_semigroup }

instance [comm_monoid α] : add_comm_monoid (additive α) :=
{ .. additive.add_monoid, .. additive.add_comm_semigroup }

instance [add_comm_monoid α] : comm_monoid (multiplicative α) :=
{ ..multiplicative.monoid, .. multiplicative.comm_semigroup }

instance [has_inv α] : has_neg (additive α) := ⟨λ x, multiplicative.of_add x.to_mul⁻¹⟩

@[simp] lemma of_mul_inv [has_inv α] (x : α) : additive.of_mul x⁻¹ = -(additive.of_mul x) := rfl

@[simp] lemma to_mul_neg [has_inv α] (x : additive α) : (-x).to_mul = x.to_mul⁻¹ := rfl

instance [has_neg α] : has_inv (multiplicative α) := ⟨λ x, additive.of_mul (-x.to_add)⟩

@[simp] lemma of_add_neg [has_neg α] (x : α) :
  multiplicative.of_add (-x) = (multiplicative.of_add x)⁻¹ := rfl

@[simp] lemma to_add_inv [has_neg α] (x : multiplicative α) :
  (x⁻¹).to_add = -x.to_add := rfl

instance additive.has_sub [has_div α] : has_sub (additive α) :=
{ sub := λ x y, additive.of_mul (x.to_mul / y.to_mul) }

instance multiplicative.has_div [has_sub α] : has_div (multiplicative α) :=
{ div := λ x y, multiplicative.of_add (x.to_add - y.to_add) }

@[simp] lemma of_add_sub [has_sub α] (x y : α) :
  multiplicative.of_add (x - y) = multiplicative.of_add x / multiplicative.of_add y :=
rfl

@[simp] lemma to_add_div [has_sub α] (x y : multiplicative α) :
  (x / y).to_add = x.to_add - y.to_add :=
rfl

@[simp] lemma of_mul_div [has_div α] (x y : α) :
  additive.of_mul (x / y) = additive.of_mul x - additive.of_mul y :=
rfl

@[simp] lemma to_mul_sub [has_div α] (x y : additive α) :
  (x - y).to_mul = x.to_mul / y.to_mul :=
rfl

instance [div_inv_monoid α] : sub_neg_monoid (additive α) :=
{ sub_eq_add_neg := @div_eq_mul_inv α _,
  gsmul := @gpow α _,
  gsmul_zero' := div_inv_monoid.gpow_zero',
  gsmul_succ' := div_inv_monoid.gpow_succ',
  gsmul_neg' := div_inv_monoid.gpow_neg',
  .. additive.has_neg, .. additive.has_sub, .. additive.add_monoid }

instance [sub_neg_monoid α] : div_inv_monoid (multiplicative α) :=
{ div_eq_mul_inv := @sub_eq_add_neg α _,
  gpow := @gsmul α _,
  gpow_zero' := sub_neg_monoid.gsmul_zero',
  gpow_succ' := sub_neg_monoid.gsmul_succ',
  gpow_neg' := sub_neg_monoid.gsmul_neg',
  .. multiplicative.has_inv, .. multiplicative.has_div, .. multiplicative.monoid }

instance [group α] : add_group (additive α) :=
{ add_left_neg := @mul_left_inv α _,
  .. additive.sub_neg_monoid }

instance [add_group α] : group (multiplicative α) :=
{ mul_left_inv := @add_left_neg α _,
  .. multiplicative.div_inv_monoid }

instance [comm_group α] : add_comm_group (additive α) :=
{ .. additive.add_group, .. additive.add_comm_monoid }

instance [add_comm_group α] : comm_group (multiplicative α) :=
{ .. multiplicative.group, .. multiplicative.comm_monoid }

/-- Reinterpret `α →+ β` as `multiplicative α →* multiplicative β`. -/
def add_monoid_hom.to_multiplicative [add_zero_class α] [add_zero_class β] :
  (α →+ β) ≃ (multiplicative α →* multiplicative β) :=
⟨λ f, ⟨f.1, f.2, f.3⟩, λ f, ⟨f.1, f.2, f.3⟩, λ x, by { ext, refl, }, λ x, by { ext, refl, }⟩

/-- Reinterpret `α →* β` as `additive α →+ additive β`. -/
def monoid_hom.to_additive [mul_one_class α] [mul_one_class β] :
  (α →* β) ≃ (additive α →+ additive β) :=
⟨λ f, ⟨f.1, f.2, f.3⟩, λ f, ⟨f.1, f.2, f.3⟩, λ x, by { ext, refl, }, λ x, by { ext, refl, }⟩

/-- Reinterpret `additive α →+ β` as `α →* multiplicative β`. -/
def add_monoid_hom.to_multiplicative' [mul_one_class α] [add_zero_class β] :
  (additive α →+ β) ≃ (α →* multiplicative β) :=
⟨λ f, ⟨f.1, f.2, f.3⟩, λ f, ⟨f.1, f.2, f.3⟩, λ x, by { ext, refl, }, λ x, by { ext, refl, }⟩

/-- Reinterpret `α →* multiplicative β` as `additive α →+ β`. -/
def monoid_hom.to_additive' [mul_one_class α] [add_zero_class β] :
  (α →* multiplicative β) ≃ (additive α →+ β) :=
add_monoid_hom.to_multiplicative'.symm

/-- Reinterpret `α →+ additive β` as `multiplicative α →* β`. -/
def add_monoid_hom.to_multiplicative'' [add_zero_class α] [mul_one_class β] :
  (α →+ additive β) ≃ (multiplicative α →* β) :=
⟨λ f, ⟨f.1, f.2, f.3⟩, λ f, ⟨f.1, f.2, f.3⟩, λ x, by { ext, refl, }, λ x, by { ext, refl, }⟩

/-- Reinterpret `multiplicative α →* β` as `α →+ additive β`. -/
def monoid_hom.to_additive'' [add_zero_class α] [mul_one_class β] :
  (multiplicative α →* β) ≃ (α →+ additive β) :=
add_monoid_hom.to_multiplicative''.symm

/-- If `α` has some multiplicative structure and coerces to a function,
then `additive α` should also coerce to the same function.

This allows `additive` to be used on bundled function types with a multiplicative structure, which
is often used for composition, without affecting the behavior of the function itself.
-/
instance additive.has_coe_to_fun {α : Type*} [has_coe_to_fun α] :
  has_coe_to_fun (additive α) :=
⟨λ a, has_coe_to_fun.F a.to_mul, λ a, coe_fn a.to_mul⟩

/-- If `α` has some additive structure and coerces to a function,
then `multiplicative α` should also coerce to the same function.

This allows `multiplicative` to be used on bundled function types with an additive structure, which
is often used for composition, without affecting the behavior of the function itself.
-/
instance multiplicative.has_coe_to_fun {α : Type*} [has_coe_to_fun α] :
  has_coe_to_fun (multiplicative α) :=
⟨λ a, has_coe_to_fun.F a.to_add, λ a, coe_fn a.to_add⟩


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

! This file was ported from Lean 3 source module data.multiset.dedup
! leanprover-community/mathlib commit f2f413b9d4be3a02840d0663dace76e8fe3da053
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Multiset.Nodup

/-!
# Erasing duplicates in a multiset.

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

namespace Multiset

open List

variable {α β : Type _} [DecidableEq α]

/-! ### dedup -/

#print Multiset.dedup /-
/-- `dedup s` removes duplicates from `s`, yielding a `nodup` multiset. -/
def dedup (s : Multiset α) : Multiset α :=
  Quot.liftOn s (fun l => (l.dedup : Multiset α)) fun s t p => Quot.sound p.dedup
#align multiset.dedup Multiset.dedup
-/

#print Multiset.coe_dedup /-
@[simp]
theorem coe_dedup (l : List α) : @dedup α _ l = l.dedup :=
  rfl
#align multiset.coe_dedup Multiset.coe_dedup
-/

#print Multiset.dedup_zero /-
@[simp]
theorem dedup_zero : @dedup α _ 0 = 0 :=
  rfl
#align multiset.dedup_zero Multiset.dedup_zero
-/

#print Multiset.mem_dedup /-
@[simp]
theorem mem_dedup {a : α} {s : Multiset α} : a ∈ dedup s ↔ a ∈ s :=
  Quot.inductionOn s fun l => mem_dedup
#align multiset.mem_dedup Multiset.mem_dedup
-/

#print Multiset.dedup_cons_of_mem /-
@[simp]
theorem dedup_cons_of_mem {a : α} {s : Multiset α} : a ∈ s → dedup (a ::ₘ s) = dedup s :=
  Quot.inductionOn s fun l m => @congr_arg _ _ _ _ coe <| dedup_cons_of_mem m
#align multiset.dedup_cons_of_mem Multiset.dedup_cons_of_mem
-/

#print Multiset.dedup_cons_of_not_mem /-
@[simp]
theorem dedup_cons_of_not_mem {a : α} {s : Multiset α} : a ∉ s → dedup (a ::ₘ s) = a ::ₘ dedup s :=
  Quot.inductionOn s fun l m => congr_arg coe <| dedup_cons_of_not_mem m
#align multiset.dedup_cons_of_not_mem Multiset.dedup_cons_of_not_mem
-/

/- warning: multiset.dedup_le -> Multiset.dedup_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Multiset.{u1} α), LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s) s
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Multiset.{u1} α), LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s) s
Case conversion may be inaccurate. Consider using '#align multiset.dedup_le Multiset.dedup_leₓ'. -/
theorem dedup_le (s : Multiset α) : dedup s ≤ s :=
  Quot.inductionOn s fun l => (dedup_sublist _).Subperm
#align multiset.dedup_le Multiset.dedup_le

#print Multiset.dedup_subset /-
theorem dedup_subset (s : Multiset α) : dedup s ⊆ s :=
  subset_of_le <| dedup_le _
#align multiset.dedup_subset Multiset.dedup_subset
-/

#print Multiset.subset_dedup /-
theorem subset_dedup (s : Multiset α) : s ⊆ dedup s := fun a => mem_dedup.2
#align multiset.subset_dedup Multiset.subset_dedup
-/

#print Multiset.dedup_subset' /-
@[simp]
theorem dedup_subset' {s t : Multiset α} : dedup s ⊆ t ↔ s ⊆ t :=
  ⟨Subset.trans (subset_dedup _), Subset.trans (dedup_subset _)⟩
#align multiset.dedup_subset' Multiset.dedup_subset'
-/

#print Multiset.subset_dedup' /-
@[simp]
theorem subset_dedup' {s t : Multiset α} : s ⊆ dedup t ↔ s ⊆ t :=
  ⟨fun h => Subset.trans h (dedup_subset _), fun h => Subset.trans h (subset_dedup _)⟩
#align multiset.subset_dedup' Multiset.subset_dedup'
-/

#print Multiset.nodup_dedup /-
@[simp]
theorem nodup_dedup (s : Multiset α) : Nodup (dedup s) :=
  Quot.inductionOn s nodup_dedup
#align multiset.nodup_dedup Multiset.nodup_dedup
-/

#print Multiset.dedup_eq_self /-
theorem dedup_eq_self {s : Multiset α} : dedup s = s ↔ Nodup s :=
  ⟨fun e => e ▸ nodup_dedup s, Quot.inductionOn s fun l h => congr_arg coe h.dedup⟩
#align multiset.dedup_eq_self Multiset.dedup_eq_self
-/

alias dedup_eq_self ↔ _ nodup.dedup
#align multiset.nodup.dedup Multiset.Nodup.dedup

#print Multiset.count_dedup /-
theorem count_dedup (m : Multiset α) (a : α) : m.dedup.count a = if a ∈ m then 1 else 0 :=
  Quot.inductionOn m fun l => count_dedup _ _
#align multiset.count_dedup Multiset.count_dedup
-/

#print Multiset.dedup_idempotent /-
@[simp]
theorem dedup_idempotent {m : Multiset α} : m.dedup.dedup = m.dedup :=
  Quot.inductionOn m fun l => @congr_arg _ _ _ _ coe dedup_idempotent
#align multiset.dedup_idempotent Multiset.dedup_idempotent
-/

#print Multiset.dedup_bind_dedup /-
@[simp]
theorem dedup_bind_dedup [DecidableEq β] (m : Multiset α) (f : α → Multiset β) :
    (m.dedup.bind f).dedup = (m.bind f).dedup :=
  by
  ext x
  simp_rw [count_dedup, mem_bind, mem_dedup]
#align multiset.dedup_bind_dedup Multiset.dedup_bind_dedup
-/

#print Multiset.dedup_eq_zero /-
theorem dedup_eq_zero {s : Multiset α} : dedup s = 0 ↔ s = 0 :=
  ⟨fun h => eq_zero_of_subset_zero <| h ▸ subset_dedup _, fun h => h.symm ▸ dedup_zero⟩
#align multiset.dedup_eq_zero Multiset.dedup_eq_zero
-/

#print Multiset.dedup_singleton /-
@[simp]
theorem dedup_singleton {a : α} : dedup ({a} : Multiset α) = {a} :=
  (nodup_singleton _).dedup
#align multiset.dedup_singleton Multiset.dedup_singleton
-/

/- warning: multiset.le_dedup -> Multiset.le_dedup is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α} {t : Multiset.{u1} α}, Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t)) (And (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) (Multiset.Nodup.{u1} α s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α} {t : Multiset.{u1} α}, Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t)) (And (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) (Multiset.Nodup.{u1} α s))
Case conversion may be inaccurate. Consider using '#align multiset.le_dedup Multiset.le_dedupₓ'. -/
theorem le_dedup {s t : Multiset α} : s ≤ dedup t ↔ s ≤ t ∧ Nodup s :=
  ⟨fun h => ⟨le_trans h (dedup_le _), nodup_of_le h (nodup_dedup _)⟩, fun ⟨l, d⟩ =>
    (le_iff_subset d).2 <| Subset.trans (subset_of_le l) (subset_dedup _)⟩
#align multiset.le_dedup Multiset.le_dedup

/- warning: multiset.le_dedup_self -> Multiset.le_dedup_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α}, Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s)) (Multiset.Nodup.{u1} α s)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α}, Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s)) (Multiset.Nodup.{u1} α s)
Case conversion may be inaccurate. Consider using '#align multiset.le_dedup_self Multiset.le_dedup_selfₓ'. -/
theorem le_dedup_self {s : Multiset α} : s ≤ dedup s ↔ Nodup s := by
  rw [le_dedup, and_iff_right le_rfl]
#align multiset.le_dedup_self Multiset.le_dedup_self

#print Multiset.dedup_ext /-
theorem dedup_ext {s t : Multiset α} : dedup s = dedup t ↔ ∀ a, a ∈ s ↔ a ∈ t := by simp [nodup.ext]
#align multiset.dedup_ext Multiset.dedup_ext
-/

#print Multiset.dedup_map_dedup_eq /-
theorem dedup_map_dedup_eq [DecidableEq β] (f : α → β) (s : Multiset α) :
    dedup (map f (dedup s)) = dedup (map f s) := by simp [dedup_ext]
#align multiset.dedup_map_dedup_eq Multiset.dedup_map_dedup_eq
-/

/- warning: multiset.dedup_nsmul -> Multiset.dedup_nsmul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α} {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{succ u1} (Multiset.{u1} α) (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) (SMul.smul.{0, u1} Nat (Multiset.{u1} α) (AddMonoid.SMul.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) n s)) (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α} {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{succ u1} (Multiset.{u1} α) (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) (HSMul.hSMul.{0, u1, u1} Nat (Multiset.{u1} α) (Multiset.{u1} α) (instHSMul.{0, u1} Nat (Multiset.{u1} α) (AddMonoid.SMul.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) n s)) (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s))
Case conversion may be inaccurate. Consider using '#align multiset.dedup_nsmul Multiset.dedup_nsmulₓ'. -/
@[simp]
theorem dedup_nsmul {s : Multiset α} {n : ℕ} (h0 : n ≠ 0) : (n • s).dedup = s.dedup :=
  by
  ext a
  by_cases h : a ∈ s <;> simp [h, h0]
#align multiset.dedup_nsmul Multiset.dedup_nsmul

/- warning: multiset.nodup.le_dedup_iff_le -> Multiset.Nodup.le_dedup_iff_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s (Multiset.dedup.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t))
Case conversion may be inaccurate. Consider using '#align multiset.nodup.le_dedup_iff_le Multiset.Nodup.le_dedup_iff_leₓ'. -/
theorem Nodup.le_dedup_iff_le {s t : Multiset α} (hno : s.Nodup) : s ≤ t.dedup ↔ s ≤ t := by
  simp [le_dedup, hno]
#align multiset.nodup.le_dedup_iff_le Multiset.Nodup.le_dedup_iff_le

end Multiset

/- warning: multiset.nodup.le_nsmul_iff_le -> Multiset.Nodup.le_nsmul_iff_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α} {n : Nat}, (Multiset.Nodup.{u1} α s) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s (SMul.smul.{0, u1} Nat (Multiset.{u1} α) (AddMonoid.SMul.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) n t)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t))
but is expected to have type
  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α} {n : Nat}, (Multiset.Nodup.{u1} α s) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s (HSMul.hSMul.{0, u1, u1} Nat (Multiset.{u1} α) (Multiset.{u1} α) (instHSMul.{0, u1} Nat (Multiset.{u1} α) (AddMonoid.SMul.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) n t)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t))
Case conversion may be inaccurate. Consider using '#align multiset.nodup.le_nsmul_iff_le Multiset.Nodup.le_nsmul_iff_leₓ'. -/
theorem Multiset.Nodup.le_nsmul_iff_le {α : Type _} {s t : Multiset α} {n : ℕ} (h : s.Nodup)
    (hn : n ≠ 0) : s ≤ n • t ↔ s ≤ t := by
  classical
    rw [← h.le_dedup_iff_le, Iff.comm, ← h.le_dedup_iff_le]
    simp [hn]
#align multiset.nodup.le_nsmul_iff_le Multiset.Nodup.le_nsmul_iff_le


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

! This file was ported from Lean 3 source module deprecated.subfield
! leanprover-community/mathlib commit bd9851ca476957ea4549eb19b40e7b5ade9428cc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Deprecated.Subring

/-!
# Unbundled subfields (deprecated)

This file is deprecated, and is no longer imported by anything in mathlib other than other
deprecated files, and test files. You should not need to import it.

This file defines predicates for unbundled subfields. Instead of using this file, please use
`Subfield`, defined in `FieldTheory.Subfield`, for subfields of fields.

## Main definitions

`IsSubfield (S : Set F) : Prop` : the predicate that `S` is the underlying set of a subfield
of the field `F`. The bundled variant `Subfield F` should be used in preference to this.

## Tags

is_subfield
-/

variable {F : Type _} [Field F] (S : Set F)

/-- `IsSubfield (S : Set F)` is the predicate saying that a given subset of a field is
the set underlying a subfield. This structure is deprecated; use the bundled variant
`Subfield F` to model subfields of a field. -/
structure IsSubfield extends IsSubring S : Prop where
  inv_mem : ∀ {x : F}, x ∈ S → x⁻¹ ∈ S
#align is_subfield IsSubfield

theorem IsSubfield.div_mem {S : Set F} (hS : IsSubfield S) {x y : F} (hx : x ∈ S) (hy : y ∈ S) :
    x / y ∈ S := by
  rw [div_eq_mul_inv]
  exact hS.toIsSubring.toIsSubmonoid.mul_mem hx (hS.inv_mem hy)
#align is_subfield.div_mem IsSubfield.div_mem

theorem IsSubfield.pow_mem {a : F} {n : ℤ} {s : Set F} (hs : IsSubfield s) (h : a ∈ s) :
    a ^ n ∈ s := by
  cases' n with n n
  · suffices a ^ (n : ℤ) ∈ s by exact this
    rw [zpow_ofNat]
    exact hs.toIsSubring.toIsSubmonoid.pow_mem h
  · rw [zpow_negSucc]
    exact hs.inv_mem (hs.toIsSubring.toIsSubmonoid.pow_mem h)
#align is_subfield.pow_mem IsSubfield.pow_mem

theorem Univ.isSubfield : IsSubfield (@Set.univ F) :=
  { Univ.isSubmonoid, IsAddSubgroup.univ_addSubgroup with
    inv_mem := fun _ ↦ trivial }
#align univ.is_subfield Univ.isSubfield

theorem Preimage.isSubfield {K : Type _} [Field K] (f : F →+* K) {s : Set K} (hs : IsSubfield s) :
    IsSubfield (f ⁻¹' s) :=
  { f.isSubring_preimage hs.toIsSubring with
    inv_mem := fun {a} (ha : f a ∈ s) ↦ show f a⁻¹ ∈ s by
      rw [map_inv₀]
      exact hs.inv_mem ha }
#align preimage.is_subfield Preimage.isSubfield

theorem Image.isSubfield {K : Type _} [Field K] (f : F →+* K) {s : Set F} (hs : IsSubfield s) :
    IsSubfield (f '' s) :=
  { f.isSubring_image hs.toIsSubring with
    inv_mem := fun ⟨x, xmem, ha⟩ ↦ ⟨x⁻¹, hs.inv_mem xmem, ha ▸ map_inv₀ f x⟩ }
#align image.is_subfield Image.isSubfield

theorem Range.isSubfield {K : Type _} [Field K] (f : F →+* K) : IsSubfield (Set.range f) := by
  rw [← Set.image_univ]
  apply Image.isSubfield _ Univ.isSubfield
#align range.is_subfield Range.isSubfield

namespace Field

/-- `Field.closure s` is the minimal subfield that includes `s`. -/
def closure : Set F :=
  { x | ∃ y ∈ Ring.closure S, ∃ z ∈ Ring.closure S, y / z = x }
#align field.closure Field.closure

variable {S}

theorem ring_closure_subset : Ring.closure S ⊆ closure S :=
  fun x hx ↦ ⟨x, hx, 1, Ring.closure.isSubring.toIsSubmonoid.one_mem, div_one x⟩
#align field.ring_closure_subset Field.ring_closure_subset

theorem closure.isSubmonoid : IsSubmonoid (closure S) :=
  { mul_mem := by
      rintro _ _ ⟨p, hp, q, hq, hq0, rfl⟩ ⟨r, hr, s, hs, hs0, rfl⟩
      exact ⟨p * r, IsSubmonoid.mul_mem Ring.closure.isSubring.toIsSubmonoid hp hr, q * s,
        IsSubmonoid.mul_mem Ring.closure.isSubring.toIsSubmonoid hq hs,
        (div_mul_div_comm _ _ _ _).symm⟩
    one_mem := ring_closure_subset <| IsSubmonoid.one_mem Ring.closure.isSubring.toIsSubmonoid }
#align field.closure.is_submonoid Field.closure.isSubmonoid

theorem closure.isSubfield : IsSubfield (closure S) :=
  { closure.isSubmonoid with
    add_mem := by
      intro a b ha hb
      rcases id ha with ⟨p, hp, q, hq, rfl⟩
      rcases id hb with ⟨r, hr, s, hs, rfl⟩
      by_cases hq0 : q = 0
      · rwa [hq0, div_zero, zero_add]
      by_cases hs0 : s = 0
      · rwa [hs0, div_zero, add_zero]
      exact ⟨p * s + q * r,
        IsAddSubmonoid.add_mem Ring.closure.isSubring.toIsAddSubgroup.toIsAddSubmonoid
          (Ring.closure.isSubring.toIsSubmonoid.mul_mem hp hs)
          (Ring.closure.isSubring.toIsSubmonoid.mul_mem hq hr),
        q * s, Ring.closure.isSubring.toIsSubmonoid.mul_mem hq hs, (div_add_div p r hq0 hs0).symm⟩
    zero_mem := ring_closure_subset Ring.closure.isSubring.toIsAddSubgroup.toIsAddSubmonoid.zero_mem
    neg_mem := by
      rintro _ ⟨p, hp, q, hq, rfl⟩
      exact ⟨-p, Ring.closure.isSubring.toIsAddSubgroup.neg_mem hp, q, hq, neg_div q p⟩
    inv_mem := by
      rintro _ ⟨p, hp, q, hq, rfl⟩
      exact ⟨q, hq, p, hp, (inv_div _ _).symm⟩ }
#align field.closure.is_subfield Field.closure.isSubfield

theorem mem_closure {a : F} (ha : a ∈ S) : a ∈ closure S :=
  ring_closure_subset <| Ring.mem_closure ha
#align field.mem_closure Field.mem_closure

theorem subset_closure : S ⊆ closure S :=
  fun _ ↦ mem_closure
#align field.subset_closure Field.subset_closure

theorem closure_subset {T : Set F} (hT : IsSubfield T) (H : S ⊆ T) : closure S ⊆ T := by
  rintro _ ⟨p, hp, q, hq, hq0, rfl⟩
  exact hT.div_mem (Ring.closure_subset hT.toIsSubring H hp)
    (Ring.closure_subset hT.toIsSubring H hq)
#align field.closure_subset Field.closure_subset

theorem closure_subset_iff {s t : Set F} (ht : IsSubfield t) : closure s ⊆ t ↔ s ⊆ t :=
  ⟨Set.Subset.trans subset_closure, closure_subset ht⟩
#align field.closure_subset_iff Field.closure_subset_iff

theorem closure_mono {s t : Set F} (H : s ⊆ t) : closure s ⊆ closure t :=
  closure_subset closure.isSubfield <| Set.Subset.trans H subset_closure
#align field.closure_mono Field.closure_mono

end Field

theorem isSubfield_unionᵢ_of_directed {ι : Type _} [Nonempty ι] {s : ι → Set F}
    (hs : ∀ i, IsSubfield (s i)) (directed : ∀ i j, ∃ k, s i ⊆ s k ∧ s j ⊆ s k) :
    IsSubfield (⋃ i, s i) :=
  { inv_mem := fun hx ↦
      let ⟨i, hi⟩ := Set.mem_unionᵢ.1 hx
      Set.mem_unionᵢ.2 ⟨i, (hs i).inv_mem hi⟩
    toIsSubring := isSubring_unionᵢ_of_directed (fun i ↦ (hs i).toIsSubring) directed }
#align is_subfield_Union_of_directed isSubfield_unionᵢ_of_directed

theorem IsSubfield.inter {S₁ S₂ : Set F} (hS₁ : IsSubfield S₁) (hS₂ : IsSubfield S₂) :
    IsSubfield (S₁ ∩ S₂) :=
  { IsSubring.inter hS₁.toIsSubring hS₂.toIsSubring with
    inv_mem := fun hx ↦ ⟨hS₁.inv_mem hx.1, hS₂.inv_mem hx.2⟩ }
#align is_subfield.inter IsSubfield.inter

theorem IsSubfield.interᵢ {ι : Sort _} {S : ι → Set F} (h : ∀ y : ι, IsSubfield (S y)) :
    IsSubfield (Set.interᵢ S) :=
  { IsSubring.interᵢ fun y ↦ (h y).toIsSubring with
    inv_mem := fun hx ↦ Set.mem_interᵢ.2 fun y ↦ (h y).inv_mem <| Set.mem_interᵢ.1 hx y }
#align is_subfield.Inter IsSubfield.interᵢ


import field_theory.algebraic_closure
import data.zmod.basic
import Rings.ToMathlib.char_p

namespace ulift.equiv

variables {k : Type*} [field k]

open ulift

def zero : ulift k := up 0

lemma zero_add (a : ulift k) :
  up (down (up 0) + down a) = a :=
begin
  rw ← up_down a,
  apply congr_arg up,
  simp only [down_up, zero_add],
end

lemma add_zero (a : ulift k) :
  up (down a + down (up 0)) = a :=
begin
  rw ← up_down a,
  apply congr_arg up,
  simp only [down_up, add_zero],
end

lemma add_comm (a b : ulift k) :
  up (down a + down b) = up (down b + down a) :=
congr_arg up (add_comm _ _)

lemma add_assoc (a b c : ulift k) :
  up (down (up (down a + down b)) + down c) =
  up (down a + down (up (down b + down c))) :=
begin
  apply congr_arg up,
  simp only [down_up, add_assoc],
end

lemma add_left_neg (a : ulift k) :
  up (down (up (- down a)) + down a) = up 0 :=
begin
  apply congr_arg up,
  simp only [down_up, add_left_neg],
end

lemma mul_one (a : ulift k) :
  up (down a * down (up 1)) = a :=
begin
  rw ← up_down a,
  apply congr_arg up,
  simp only [down_up, mul_one],
end

lemma one_mul (a : ulift k) :
  up (down (up 1) * down a) = a :=
begin
  rw ← up_down a,
  apply congr_arg up,
  simp only [down_up, one_mul],
end

lemma mul_comm (a b : ulift k) :
  up (down a * down b) = up (down b * down a) :=
congr_arg up (mul_comm _ _)

lemma mul_assoc (a b c : ulift k) :
  up (down (up (down a * down b)) * down c) =
  up (down a * down (up (down b * down c))) :=
begin
  apply congr_arg up,
  simp only [down_up, mul_assoc],
end

lemma left_distrib (a b c : ulift k) :
  down a * (down b + down c)
  = down a * down b + down a * down c :=
begin
  sorry
end

def field [field k] : field (ulift k) :=
equiv.field (equiv.ulift)

-- { zero             := up 0,
--   add              := λ a b, up (down a + down b),
--   neg              := λ a, up (- down a),
--   one              := up 1,
--   mul              := λ a b, up (down a * down b),
--   inv              := λ a, up ((down a)⁻¹),
--   zero_add         := zero_add,
--   add_zero         := add_zero,
--   add_comm         := add_comm,
--   add_assoc        := add_assoc,
--   add_left_neg     := add_left_neg,
--   mul_one          := mul_one,
--   one_mul          := one_mul,
--   mul_comm         := mul_comm,
--   mul_assoc        := mul_assoc,
--   left_distrib     := sorry,
--   right_distrib    := sorry,
--   exists_pair_ne   := sorry,
--   mul_inv_cancel   := sorry,
--   inv_zero         := sorry }

end ulift

namespace is_alg_closed

open polynomial

lemma of_exists_root_nat_degree {k : Type*} [field k] (H : ∀ p : polynomial k, p.monic → irreducible p → nat_degree p ≠ 0 → ∃ x, p.eval x = 0) :
  is_alg_closed k :=
begin
  apply of_exists_root,
  intros p hmonic hirr,
  by_cases hdeg : nat_degree p = 0,
  {
    rw monic.degree_eq_zero_iff_eq_one hmonic at hdeg,
    rw hdeg at hirr,
    exfalso,
    apply hirr.1,
    exact ⟨ 1 , rfl ⟩,
  },
  apply H p hmonic hirr hdeg,
end

lemma of_nat_degree_ne_zero_exists_root {k : Type*} [field k]
  (H : ∀ p : polynomial k, nat_degree p ≠ 0 → ∃ x, p.eval x = 0) :
  is_alg_closed k :=
of_exists_root_nat_degree $ λ _ _ hdeg, H _

end is_alg_closed

namespace algebraic_closure

section zmod

variables (p : ℕ) [hp : fact (nat.prime p)]

include hp

@[reducible] def of_zmod := algebraic_closure (zmod p)

-- noncomputable lemma of_zmod.field : field (of_zmod p) :=
-- by apply_instance

-- noncomputable lemma of_zmod_algebra : (zmod p) →+* (of_zmod p) :=
-- algebra_map _ _

lemma of_zmod.char_p :
  char_p (of_zmod p) p :=
(ring_hom.char_p_iff_char_p (algebra_map (zmod p) (of_zmod p)) p).1 $ zmod.char_p p

@[reducible] def ulift_of_zmod : Type* := ulift (of_zmod p)

end zmod

end algebraic_closure