import minimal_sub_pq

universe u

section minimal_sub_pq_gen_group

variables {G : Type u} [group G]

inductive generated_by (gen : set G) : G → Prop
| unit : generated_by 1
| incl (x : G) (hx : x ∈ gen) : generated_by x
| mul (x y : G) (hx : generated_by x) (hy : generated_by y) : generated_by (x * y)
| inv (x : G) (hx : generated_by x) : generated_by (x⁻¹)

def group_generated_by (gen : set G) : Prop := ∀ g, generated_by gen g

def gen_set_counit (gen : set G) : (pq_group (free_gen_group_sub_pq gen)) →* G :=
begin
  fapply pq_morph_to_L_morph_adj,
  {
    intro x,
    cases x with x hx,
    exact x,
  },
  {
    split,
    {
      intros a b,
      cases a with a ha,
      cases b with b hb,
      have ab_rw : (⟨a, ha⟩▷⟨b, hb⟩ : {x // x ∈ has_coe_t_aux.coe (free_gen_group_sub_pq gen)}) = ⟨a ▷ b, _⟩ := rfl,
      rw ab_rw,
    },
    {
      intros a n,
      cases a with a ha,
      have an_rw : (⟨a, ha⟩ ^ n : {x // x ∈ has_coe_t_aux.coe (free_gen_group_sub_pq gen)}) = ⟨a ^ n, _⟩ := rfl,
      rw an_rw,
    }
  }
end

lemma gen_set_counit_of (gen : set G) (x : free_gen_group_sub_pq gen) : gen_set_counit gen (of x) = x :=
begin
  unfold gen_set_counit,
  rw pq_morph_to_L_morph_adj_comm_of,
  cases x with x hx,
  simp only [subtype.coe_mk],
end

theorem gen_set_counit_surjective (gen : set G) (hG : group_generated_by gen) : function.surjective (gen_set_counit gen) :=
begin
  intro x,
  specialize hG x,
  induction hG,
  {
    use 1,
    refl,
  },
  {
    fconstructor,
    apply of,
    fconstructor,
    exact hG_x,
    apply gen_in_free_group_sub_pq_carrier,
    exact hG_hx,
    refl,
  },
  {
    cases hG_ih_hx with a ha,
    cases hG_ih_hy with b hb,
    use a * b,
    simp only [monoid_hom.map_mul],
    rw ha,
    rw hb,
  },
  {
    cases hG_ih with a ha,
    use a⁻¹,
    simp only [inv_inj, monoid_hom.map_inv],
    assumption,
  },
end

def pq_gen_of {gen : set G} (g : G) (hg : g ∈ gen) : pq_group (free_gen_group_sub_pq gen) :=
begin
  apply of,
  fconstructor,
  exact g,
  fconstructor,
  exact hg,
end

lemma pq_gen_of_def (gen : set G) (g : G) (hg : g ∈ gen) : pq_gen_of g hg = of ⟨g, gen_in_free_group_sub_pq_carrier gen hg⟩ := rfl

def free_pq_gen_of {gen : set G} (g : G) (hg : free_group_sub_pq_carrier gen g) : pq_group (free_gen_group_sub_pq gen) :=
begin
  apply of,
  fconstructor,
  exact g,
  exact hg,
end

lemma free_pq_gen_of_def (gen : set G) (g : G) (hg : free_group_sub_pq_carrier gen g) : free_pq_gen_of g hg = of ⟨g, hg⟩ := rfl

lemma free_pq_gen_of_rhd (gen : set G) (x y : G) (hx : free_group_sub_pq_carrier gen x) (hy : free_group_sub_pq_carrier gen y) :
free_pq_gen_of (x ▷ y) (free_group_sub_pq_carrier.rhd x y hx hy) = (free_pq_gen_of x hx) ▷ (free_pq_gen_of y hy) :=
begin
  unfold free_pq_gen_of,
  rw rhd_of_eq_of_rhd,
  apply congr_arg,
  refl,
end

lemma free_pq_gen_of_pow (gen : set G) (x : G) (n : ℤ) (hx : free_group_sub_pq_carrier gen x) :
free_pq_gen_of (x ^ n) (free_group_sub_pq_carrier.pow x n hx) = (free_pq_gen_of x hx) ^ n :=
begin
  unfold free_pq_gen_of,
  rw ←of_pow_eq_pow_of,
  apply congr_arg,
  refl,
end

lemma free_pq_gen_of_one (gen : set G) : @free_pq_gen_of _ _ gen (1 : G) (free_group_sub_pq_carrier.one) = 1 :=
begin
  unfold free_pq_gen_of,
  rw ←of_one,
  apply congr_arg,
  refl,
end

lemma generated_by_rhd (gen : set G) (x y : G) (hx : generated_by gen x) (hy : generated_by gen y) : generated_by gen (x ▷ y) :=
begin
  apply generated_by.mul,
  apply generated_by.mul,
  assumption,
  assumption,
  apply generated_by.inv,
  assumption,
end

lemma generated_by_pow_nat (gen : set G) (x : G) (n : ℕ) (hx : generated_by gen x) : generated_by gen (x ^ n) :=
begin
  induction n,
  apply generated_by.unit,
  rw pow_succ,
  apply generated_by.mul,
  assumption,
  assumption,
end

lemma generated_by_pow (gen : set G) (x : G) (n : ℤ) (hx : generated_by gen x) : generated_by gen (x ^ n) :=
begin
  induction n,
  {
    apply generated_by_pow_nat,
    assumption,
  },
  {
    rw gpow_neg_succ_of_nat,
    apply generated_by.inv,
    apply generated_by_pow_nat,
    assumption,
  },
end

lemma generated_by_one (gen : set G) : generated_by gen 1 :=
begin
  apply generated_by.unit,
end

def pq_gen_set_gen_set (gen : set G) : set (pq_group (free_gen_group_sub_pq gen)) := λ x : pq_group (free_gen_group_sub_pq gen),
∃ g ∈ gen, x = pq_gen_of g H

theorem pq_gen_set_generated (gen : set G) : group_generated_by (pq_gen_set_gen_set gen) :=
begin
  intro x,
  induction x,
  {
    rw quot_mk_helper,
    induction x,
    {
      rw incl_unit_eq_unit,
      apply generated_by.unit,
    },
    {
      rw ←of_def,
      cases x with x hx,
      rw ←free_pq_gen_of_def,
      induction hx,
      {
        apply generated_by.incl,
        use hx_x,
        use hx_hx,
        refl,
      },
      {
        simp_rw ←rhd_def_group,
        suffices : generated_by (pq_gen_set_gen_set gen) ((free_pq_gen_of hx_x hx_hx) ▷ (free_pq_gen_of hx_y hx_hy)),
        convert this,
        {
          rw ←free_pq_gen_of_rhd,
          refl,
        },
        apply generated_by_rhd,
        assumption,
        assumption,
      },
      {
        suffices : generated_by (pq_gen_set_gen_set gen) ((free_pq_gen_of hx_x hx_hx) ^ hx_n),
        convert this,
        {
          rw ←free_pq_gen_of_pow,
        },
        apply generated_by_pow,
        assumption,
      },
      {
        suffices : generated_by (pq_gen_set_gen_set gen) (1),
        convert this,
        {
          rw free_pq_gen_of_one,
        },
        apply generated_by_one,
      },
    },
    {
      rw ←mul_def,
      apply generated_by.mul,
      assumption,
      assumption,
    },
    {
      rw ←inv_def,
      apply generated_by.inv,
      assumption,
    },
  },
  {refl,},
end

/-
theorem gen_set_counit_injective (gen : set G) (hG : group_generated_by gen) : function.injective (gen_set_counit gen) :=
begin
  refine (gen_set_counit gen).injective_iff.mpr _,
  intros x hx,

end
-/

end minimal_sub_pq_gen_group


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

! This file was ported from Lean 3 source module order.hom.basic
! leanprover-community/mathlib commit 62a5626868683c104774de8d85b9855234ac807c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Logic.Equiv.Option
import Mathlib.Order.RelIso.Basic
import Mathlib.Order.Disjoint
import Mathlib.Order.WithBot
import Mathlib.Tactic.Monotonicity.Attr
import Mathlib.Tactic.Replace

/-!
# Order homomorphisms

This file defines order homomorphisms, which are bundled monotone functions. A preorder
homomorphism `f : α →o β` is a function `α → β` along with a proof that `∀ x y, x ≤ y → f x ≤ f y`.

## Main definitions

In this file we define the following bundled monotone maps:
 * `OrderHom α β` a.k.a. `α →o β`: Preorder homomorphism.
    An `OrderHom α β` is a function `f : α → β` such that `a₁ ≤ a₂ → f a₁ ≤ f a₂`
 * `OrderEmbedding α β` a.k.a. `α ↪o β`: Relation embedding.
    An `OrderEmbedding α β` is an embedding `f : α ↪ β` such that `a ≤ b ↔ f a ≤ f b`.
    Defined as an abbreviation of `@RelEmbedding α β (≤) (≤)`.
* `OrderIso`: Relation isomorphism.
    An `OrderIso α β` is an equivalence `f : α ≃ β` such that `a ≤ b ↔ f a ≤ f b`.
    Defined as an abbreviation of `@RelIso α β (≤) (≤)`.

We also define many `OrderHom`s. In some cases we define two versions, one with `ₘ` suffix and
one without it (e.g., `OrderHom.compₘ` and `OrderHom.comp`). This means that the former
function is a "more bundled" version of the latter. We can't just drop the "less bundled" version
because the more bundled version usually does not work with dot notation.

* `OrderHom.id`: identity map as `α →o α`;
* `OrderHom.curry`: an order isomorphism between `α × β →o γ` and `α →o β →o γ`;
* `OrderHom.comp`: composition of two bundled monotone maps;
* `OrderHom.compₘ`: composition of bundled monotone maps as a bundled monotone map;
* `OrderHom.const`: constant function as a bundled monotone map;
* `OrderHom.prod`: combine `α →o β` and `α →o γ` into `α →o β × γ`;
* `OrderHom.prodₘ`: a more bundled version of `OrderHom.prod`;
* `OrderHom.prodIso`: order isomorphism between `α →o β × γ` and `(α →o β) × (α →o γ)`;
* `OrderHom.diag`: diagonal embedding of `α` into `α × α` as a bundled monotone map;
* `OrderHom.onDiag`: restrict a monotone map `α →o α →o β` to the diagonal;
* `OrderHom.fst`: projection `prod.fst : α × β → α` as a bundled monotone map;
* `OrderHom.snd`: projection `prod.snd : α × β → β` as a bundled monotone map;
* `OrderHom.prodMap`: `prod.map f g` as a bundled monotone map;
* `Pi.evalOrderHom`: evaluation of a function at a point `Function.eval i` as a bundled
  monotone map;
* `OrderHom.coeFnHom`: coercion to function as a bundled monotone map;
* `OrderHom.apply`: application of a `OrderHom` at a point as a bundled monotone map;
* `OrderHom.pi`: combine a family of monotone maps `f i : α →o π i` into a monotone map
  `α →o Π i, π i`;
* `OrderHom.piIso`: order isomorphism between `α →o Π i, π i` and `Π i, α →o π i`;
* `OrderHom.subtype.val`: embedding `Subtype.val : Subtype p → α` as a bundled monotone map;
* `OrderHom.dual`: reinterpret a monotone map `α →o β` as a monotone map `αᵒᵈ →o βᵒᵈ`;
* `OrderHom.dualIso`: order isomorphism between `α →o β` and `(αᵒᵈ →o βᵒᵈ)ᵒᵈ`;
* `OrderHom.compl`: order isomorphism `α ≃o αᵒᵈ` given by taking complements in a
  boolean algebra;

We also define two functions to convert other bundled maps to `α →o β`:

* `OrderEmbedding.toOrderHom`: convert `α ↪o β` to `α →o β`;
* `RelHom.toOrderHom`: convert a `RelHom` between strict orders to an `OrderHom`.

## Tags

monotone map, bundled morphism
-/

open OrderDual

variable {F α β γ δ : Type _}

/-- Bundled monotone (aka, increasing) function -/
structure OrderHom (α β : Type _) [Preorder α] [Preorder β] where
  /-- The underlying funcrion of an `OrderHom`. -/
  toFun : α → β
  /-- The underlying function of an `OrderHom` is monotone. -/
  monotone' : Monotone toFun
#align order_hom OrderHom

/-- Notation for an `OrderHom`. -/
infixr:25 " →o " => OrderHom

attribute [coe] OrderHom.toFun

/-- An order embedding is an embedding `f : α ↪ β` such that `a ≤ b ↔ (f a) ≤ (f b)`.
This definition is an abbreviation of `RelEmbedding (≤) (≤)`. -/
abbrev OrderEmbedding (α β : Type _) [LE α] [LE β] :=
  @RelEmbedding α β (· ≤ ·) (· ≤ ·)
#align order_embedding OrderEmbedding

/-- Notation for an `OrderEmbedding`. -/
infixl:25 " ↪o " => OrderEmbedding

/-- An order isomorphism is an equivalence such that `a ≤ b ↔ (f a) ≤ (f b)`.
This definition is an abbreviation of `RelIso (≤) (≤)`. -/
abbrev OrderIso (α β : Type _) [LE α] [LE β] :=
  @RelIso α β (· ≤ ·) (· ≤ ·)
#align order_iso OrderIso

/-- Notation for an `OrderIso`. -/
infixl:25 " ≃o " => OrderIso

section

/-- `OrderHomClass F α b` asserts that `F` is a type of `≤`-preserving morphisms. -/
abbrev OrderHomClass (F : Type _) (α β : outParam (Type _)) [LE α] [LE β] :=
  RelHomClass F ((· ≤ ·) : α → α → Prop) ((· ≤ ·) : β → β → Prop)
#align order_hom_class OrderHomClass

/-- `OrderIsoClass F α β` states that `F` is a type of order isomorphisms.

You should extend this class when you extend `OrderIso`. -/
class OrderIsoClass (F : Type _) (α β : outParam (Type _)) [LE α] [LE β] extends
  EquivLike F α β where
  /-- An order isomorphism respects `≤`. -/
  map_le_map_iff (f : F) {a b : α} : f a ≤ f b ↔ a ≤ b
#align order_iso_class OrderIsoClass

end

export OrderIsoClass (map_le_map_iff)

attribute [simp] map_le_map_iff

/-- Turn an element of a type `F` satisfying `OrderIsoClass F α β` into an actual
`OrderIso`. This is declared as the default coercion from `F` to `α ≃o β`. -/
@[coe]
def OrderIsoClass.toOrderIso {_ : LE α} {_ : LE β} [OrderIsoClass F α β] (f : F) : α ≃o β :=
{ EquivLike.toEquiv f with map_rel_iff' := map_le_map_iff f }

/-- Any type satisfying `OrderIsoClass` can be cast into `OrderIso` via
`OrderIsoClass.toOrderIso`. -/
instance {_ : LE α} {_ : LE β} [OrderIsoClass F α β] : CoeTC F (α ≃o β) :=
  ⟨OrderIsoClass.toOrderIso⟩

-- See note [lower instance priority]
instance (priority := 100) OrderIsoClass.toOrderHomClass {_ : LE α} {_ : LE β}
    [OrderIsoClass F α β] : OrderHomClass F α β :=
  { EquivLike.toEmbeddingLike with
    map_rel := fun f _ _ => (map_le_map_iff f).2 }
#align order_iso_class.to_order_hom_class OrderIsoClass.toOrderHomClass

namespace OrderHomClass

variable [Preorder α] [Preorder β] [OrderHomClass F α β]

protected theorem monotone (f : F) : Monotone f := fun _ _ => map_rel f
#align order_hom_class.monotone OrderHomClass.monotone

protected theorem mono (f : F) : Monotone f := fun _ _ => map_rel f
#align order_hom_class.mono OrderHomClass.mono

/-- Turn an element of a type `F` satisfying `OrderHomClass F α β` into an actual
`OrderHomClass`. This is declared as the default coercion from `F` to `α →o β`. -/
@[coe]
def toOrderHom (f : F) : α →o β where
  toFun := f
  monotone' := OrderHomClass.monotone f

/-- Any type satisfying `OrderHomClass` can be cast into `OrderHom` via
`OrderHomClass.toOrderHom`. -/
instance : CoeTC F (α →o β) :=
  ⟨toOrderHom⟩

end OrderHomClass

section OrderIsoClass

section LE

variable [LE α] [LE β] [OrderIsoClass F α β]

-- Porting note: needed to add explicit arguments to map_le_map_iff
@[simp]
theorem map_inv_le_iff (f : F) {a : α} {b : β} : EquivLike.inv f b ≤ a ↔ b ≤ f a := by
  convert (@map_le_map_iff _ _ _ _ _ _ f (EquivLike.inv f b) a).symm
  exact (EquivLike.right_inv f _).symm
#align map_inv_le_iff map_inv_le_iff

-- Porting note: needed to add explicit arguments to map_le_map_iff
@[simp]
theorem le_map_inv_iff (f : F) {a : α} {b : β} : a ≤ EquivLike.inv f b ↔ f a ≤ b := by
  convert (@map_le_map_iff _ _ _ _ _ _ f a (EquivLike.inv f b)).symm
  exact (EquivLike.right_inv _ _).symm
#align le_map_inv_iff le_map_inv_iff

end LE

variable [Preorder α] [Preorder β] [OrderIsoClass F α β]

theorem map_lt_map_iff (f : F) {a b : α} : f a < f b ↔ a < b :=
  lt_iff_lt_of_le_iff_le' (map_le_map_iff f) (map_le_map_iff f)
#align map_lt_map_iff map_lt_map_iff

@[simp]
theorem map_inv_lt_iff (f : F) {a : α} {b : β} : EquivLike.inv f b < a ↔ b < f a := by
  rw [← map_lt_map_iff f]
  simp only [EquivLike.apply_inv_apply]
#align map_inv_lt_iff map_inv_lt_iff

@[simp]
theorem lt_map_inv_iff (f : F) {a : α} {b : β} : a < EquivLike.inv f b ↔ f a < b := by
  rw [← map_lt_map_iff f]
  simp only [EquivLike.apply_inv_apply]
#align lt_map_inv_iff lt_map_inv_iff

end OrderIsoClass

namespace OrderHom

variable [Preorder α] [Preorder β] [Preorder γ] [Preorder δ]

/-- Helper instance for when there's too many metavariables to apply the coercion via `FunLike`
directly.
Remark(Floris): I think this instance is a really bad idea because now applications of
`FunLike.coe` are not being simplified by `simp`, unlike all other hom-classes.
Todo: fix after port.-/
instance : CoeFun (α →o β) fun _ => α → β :=
  ⟨OrderHom.toFun⟩

protected theorem monotone (f : α →o β) : Monotone f :=
  f.monotone'
#align order_hom.monotone OrderHom.monotone

protected theorem mono (f : α →o β) : Monotone f :=
  f.monotone
#align order_hom.mono OrderHom.mono

instance : OrderHomClass (α →o β) α β where
  coe := toFun
  coe_injective' f g h := by
    cases f
    cases g
    congr
  map_rel f _ _ h := f.monotone' h

/-- See Note [custom simps projection]. We give this manually so that we use `toFun` as the
projection directly instead. -/
def Simps.coe (f : α →o β) : α → β := f

/- Todo: all other FunLike classes use `apply` instead of `coe`
for the projection names. Maybe we should change this. -/
initialize_simps_projections OrderHom (toFun → coe)

-- Porting note: dropped `to_fun_eq_coe` as it is a tautology now.
#noalign order_hom.to_fun_eq_coe

-- Porting note: no longer good as a simp lemma, as after `whnfR` the LHS is just `f` anyway.
theorem coe_fun_mk {f : α → β} (hf : Monotone f) : (mk f hf : α → β) = f :=
  rfl
#align order_hom.coe_fun_mk OrderHom.coe_fun_mk

-- See library note [partially-applied ext lemmas]
@[ext]
theorem ext (f g : α →o β) (h : (f : α → β) = g) : f = g :=
  FunLike.coe_injective h
#align order_hom.ext OrderHom.ext

#noalign order_hom.coe_eq

/-- One can lift an unbundled monotone function to a bundled one. -/
instance : CanLift (α → β) (α →o β) (↑) Monotone where
  prf f h := ⟨⟨f, h⟩, rfl⟩
#align order_hom.monotone.can_lift OrderHom.instCanLiftForAllOrderHomToFunMonotone

/-- Copy of an `OrderHom` with a new `toFun` equal to the old one. Useful to fix definitional
equalities. -/
protected def copy (f : α →o β) (f' : α → β) (h : f' = f) : α →o β :=
  ⟨f', h.symm.subst f.monotone'⟩
#align order_hom.copy OrderHom.copy

@[simp]
theorem coe_copy (f : α →o β) (f' : α → β) (h : f' = f) : (f.copy f' h) = f' :=
  rfl
#align order_hom.coe_copy OrderHom.coe_copy

theorem copy_eq (f : α →o β) (f' : α → β) (h : f' = f) : f.copy f' h = f :=
  FunLike.ext' h
#align order_hom.copy_eq OrderHom.copy_eq

/-- The identity function as bundled monotone function. -/
@[simps (config := { fullyApplied := false })]
def id : α →o α :=
  ⟨_root_.id, monotone_id⟩
#align order_hom.id OrderHom.id
#align order_hom.id_coe OrderHom.id_coe

instance : Inhabited (α →o α) :=
  ⟨id⟩

/-- The preorder structure of `α →o β` is pointwise inequality: `f ≤ g ↔ ∀ a, f a ≤ g a`. -/
instance : Preorder (α →o β) :=
  @Preorder.lift (α →o β) (α → β) _ toFun

instance {β : Type _} [PartialOrder β] : PartialOrder (α →o β) :=
  @PartialOrder.lift (α →o β) (α → β) _ toFun ext

theorem le_def {f g : α →o β} : f ≤ g ↔ ∀ x, f x ≤ g x :=
  Iff.rfl
#align order_hom.le_def OrderHom.le_def

@[simp, norm_cast]
theorem coe_le_coe {f g : α →o β} : (f : α → β) ≤ g ↔ f ≤ g :=
  Iff.rfl
#align order_hom.coe_le_coe OrderHom.coe_le_coe

@[simp]
theorem mk_le_mk {f g : α → β} {hf hg} : mk f hf ≤ mk g hg ↔ f ≤ g :=
  Iff.rfl
#align order_hom.mk_le_mk OrderHom.mk_le_mk

@[mono]
theorem apply_mono {f g : α →o β} {x y : α} (h₁ : f ≤ g) (h₂ : x ≤ y) : f x ≤ g y :=
  (h₁ x).trans <| g.mono h₂
#align order_hom.apply_mono OrderHom.apply_mono

/-- Curry/uncurry as an order isomorphism between `α × β →o γ` and `α →o β →o γ`. -/
def curry : (α × β →o γ) ≃o (α →o β →o γ) where
  toFun f :=
    ⟨fun x => ⟨Function.curry f x, fun y₁ y₂ h => f.mono ⟨le_rfl, h⟩⟩, fun x₁ x₂ h y =>
      f.mono ⟨h, le_rfl⟩⟩
  invFun f :=
    ⟨Function.uncurry fun x => f x, fun x y h => (f.mono h.1 x.2).trans <| (f y.1).mono h.2⟩
  left_inv f := by
    ext ⟨x, y⟩
    rfl
  right_inv f := by
    ext (x y)
    rfl
  map_rel_iff' := by simp [le_def]
#align order_hom.curry OrderHom.curry

@[simp]
theorem curry_apply (f : α × β →o γ) (x : α) (y : β) : curry f x y = f (x, y) :=
  rfl
#align order_hom.curry_apply OrderHom.curry_apply

@[simp]
theorem curry_symm_apply (f : α →o β →o γ) (x : α × β) : curry.symm f x = f x.1 x.2 :=
  rfl
#align order_hom.curry_symm_apply OrderHom.curry_symm_apply

/-- The composition of two bundled monotone functions. -/
@[simps (config := { fullyApplied := false })]
def comp (g : β →o γ) (f : α →o β) : α →o γ :=
  ⟨g ∘ f, g.mono.comp f.mono⟩
#align order_hom.comp OrderHom.comp
#align order_hom.comp_coe OrderHom.comp_coe

@[mono]
theorem comp_mono ⦃g₁ g₂ : β →o γ⦄ (hg : g₁ ≤ g₂) ⦃f₁ f₂ : α →o β⦄ (hf : f₁ ≤ f₂) :
    g₁.comp f₁ ≤ g₂.comp f₂ := fun _ => (hg _).trans (g₂.mono <| hf _)
#align order_hom.comp_mono OrderHom.comp_mono

/-- The composition of two bundled monotone functions, a fully bundled version. -/
@[simps! (config := { fullyApplied := false })]
def compₘ : (β →o γ) →o (α →o β) →o α →o γ :=
  curry ⟨fun f : (β →o γ) × (α →o β) => f.1.comp f.2, fun _ _ h => comp_mono h.1 h.2⟩
#align order_hom.compₘ OrderHom.compₘ
#align order_hom.compₘ_coe_coe_coe OrderHom.compₘ_coe_coe_coe

@[simp]
theorem comp_id (f : α →o β) : comp f id = f := by
  ext
  rfl
#align order_hom.comp_id OrderHom.comp_id

@[simp]
theorem id_comp (f : α →o β) : comp id f = f := by
  ext
  rfl
#align order_hom.id_comp OrderHom.id_comp

/-- Constant function bundled as a `OrderHom`. -/
@[simps (config := { fullyApplied := false })]
def const (α : Type _) [Preorder α] {β : Type _} [Preorder β] : β →o α →o β where
  toFun b := ⟨Function.const α b, fun _ _ _ => le_rfl⟩
  monotone' _ _  h _ := h
#align order_hom.const OrderHom.const
#align order_hom.const_coe_coe OrderHom.const_coe_coe

@[simp]
theorem const_comp (f : α →o β) (c : γ) : (const β c).comp f = const α c :=
  rfl
#align order_hom.const_comp OrderHom.const_comp

@[simp]
theorem comp_const (γ : Type _) [Preorder γ] (f : α →o β) (c : α) :
    f.comp (const γ c) = const γ (f c) :=
  rfl
#align order_hom.comp_const OrderHom.comp_const

/-- Given two bundled monotone maps `f`, `g`, `f.prod g` is the map `x ↦ (f x, g x)` bundled as a
`OrderHom`. -/
@[simps]
protected def prod (f : α →o β) (g : α →o γ) : α →o β × γ :=
  ⟨fun x => (f x, g x), fun _ _ h => ⟨f.mono h, g.mono h⟩⟩
#align order_hom.prod OrderHom.prod
#align order_hom.prod_coe OrderHom.prod_coe

@[mono]
theorem prod_mono {f₁ f₂ : α →o β} (hf : f₁ ≤ f₂) {g₁ g₂ : α →o γ} (hg : g₁ ≤ g₂) :
    f₁.prod g₁ ≤ f₂.prod g₂ := fun _ => Prod.le_def.2 ⟨hf _, hg _⟩
#align order_hom.prod_mono OrderHom.prod_mono

theorem comp_prod_comp_same (f₁ f₂ : β →o γ) (g : α →o β) :
    (f₁.comp g).prod (f₂.comp g) = (f₁.prod f₂).comp g :=
  rfl
#align order_hom.comp_prod_comp_same OrderHom.comp_prod_comp_same

/-- Given two bundled monotone maps `f`, `g`, `f.prod g` is the map `x ↦ (f x, g x)` bundled as a
`OrderHom`. This is a fully bundled version. -/
@[simps!]
def prodₘ : (α →o β) →o (α →o γ) →o α →o β × γ :=
  curry ⟨fun f : (α →o β) × (α →o γ) => f.1.prod f.2, fun _ _ h => prod_mono h.1 h.2⟩
#align order_hom.prodₘ OrderHom.prodₘ
#align order_hom.prodₘ_coe_coe_coe OrderHom.prodₘ_coe_coe_coe

/-- Diagonal embedding of `α` into `α × α` as a `OrderHom`. -/
@[simps!]
def diag : α →o α × α :=
  id.prod id
#align order_hom.diag OrderHom.diag
#align order_hom.diag_coe OrderHom.diag_coe

/-- Restriction of `f : α →o α →o β` to the diagonal. -/
@[simps! (config := { simpRhs := true })]
def onDiag (f : α →o α →o β) : α →o β :=
  (curry.symm f).comp diag
#align order_hom.on_diag OrderHom.onDiag
#align order_hom.on_diag_coe OrderHom.onDiag_coe

/-- `Prod.fst` as a `OrderHom`. -/
@[simps]
def fst : α × β →o α :=
  ⟨Prod.fst, fun _ _ h => h.1⟩
#align order_hom.fst OrderHom.fst
#align order_hom.fst_coe OrderHom.fst_coe

/-- `Prod.snd` as a `OrderHom`. -/
@[simps]
def snd : α × β →o β :=
  ⟨Prod.snd, fun _ _ h => h.2⟩
#align order_hom.snd OrderHom.snd
#align order_hom.snd_coe OrderHom.snd_coe

@[simp]
theorem fst_prod_snd : (fst : α × β →o α).prod snd = id := by
  ext ⟨x, y⟩ : 2
  rfl
#align order_hom.fst_prod_snd OrderHom.fst_prod_snd

@[simp]
theorem fst_comp_prod (f : α →o β) (g : α →o γ) : fst.comp (f.prod g) = f :=
  ext _ _ rfl
#align order_hom.fst_comp_prod OrderHom.fst_comp_prod

@[simp]
theorem snd_comp_prod (f : α →o β) (g : α →o γ) : snd.comp (f.prod g) = g :=
  ext _ _ rfl
#align order_hom.snd_comp_prod OrderHom.snd_comp_prod

/-- Order isomorphism between the space of monotone maps to `β × γ` and the product of the spaces
of monotone maps to `β` and `γ`. -/
@[simps]
def prodIso : (α →o β × γ) ≃o (α →o β) × (α →o γ) where
  toFun f := (fst.comp f, snd.comp f)
  invFun f := f.1.prod f.2
  left_inv f := by ext <;> rfl
  right_inv f := by ext <;> rfl
  map_rel_iff' := forall_and.symm
#align order_hom.prod_iso OrderHom.prodIso
#align order_hom.prod_iso_apply OrderHom.prodIso_apply
#align order_hom.prod_iso_symm_apply OrderHom.prodIso_symm_apply

/-- `Prod.map` of two `OrderHom`s as a `OrderHom`. -/
@[simps]
def prodMap (f : α →o β) (g : γ →o δ) : α × γ →o β × δ :=
  ⟨Prod.map f g, fun _ _ h => ⟨f.mono h.1, g.mono h.2⟩⟩
#align order_hom.prod_map OrderHom.prodMap
#align order_hom.prod_map_coe OrderHom.prodMap_coe

variable {ι : Type _} {π : ι → Type _} [∀ i, Preorder (π i)]

/-- Evaluation of an unbundled function at a point (`Function.eval`) as a `OrderHom`. -/
@[simps (config := { fullyApplied := false })]
def _root_.Pi.evalOrderHom (i : ι) : (∀ j, π j) →o π i :=
  ⟨Function.eval i, Function.monotone_eval i⟩
#align pi.eval_order_hom Pi.evalOrderHom
#align pi.eval_order_hom_coe Pi.evalOrderHom_coe

/-- The "forgetful functor" from `α →o β` to `α → β` that takes the underlying function,
is monotone. -/
@[simps (config := { fullyApplied := false })]
def coeFnHom : (α →o β) →o α → β where
  toFun f := f
  monotone' _ _ h := h
#align order_hom.coe_fn_hom OrderHom.coeFnHom
#align order_hom.coe_fn_hom_coe OrderHom.coeFnHom_coe

/-- Function application `fun f => f a` (for fixed `a`) is a monotone function from the
monotone function space `α →o β` to `β`. See also `Pi.evalOrderHom`.  -/
@[simps! (config := { fullyApplied := false })]
def apply (x : α) : (α →o β) →o β :=
  (Pi.evalOrderHom x).comp coeFnHom
#align order_hom.apply OrderHom.apply
#align order_hom.apply_coe OrderHom.apply_coe

/-- Construct a bundled monotone map `α →o Π i, π i` from a family of monotone maps
`f i : α →o π i`. -/
@[simps]
def pi (f : ∀ i, α →o π i) : α →o ∀ i, π i :=
  ⟨fun x i => f i x, fun _ _ h i => (f i).mono h⟩
#align order_hom.pi OrderHom.pi
#align order_hom.pi_coe OrderHom.pi_coe

/-- Order isomorphism between bundled monotone maps `α →o Π i, π i` and families of bundled monotone
maps `Π i, α →o π i`. -/
@[simps]
def piIso : (α →o ∀ i, π i) ≃o ∀ i, α →o π i where
  toFun f i := (Pi.evalOrderHom i).comp f
  invFun := pi
  left_inv f := by
    ext (x i)
    rfl
  right_inv f := by
    ext (x i)
    rfl
  map_rel_iff' := forall_swap
#align order_hom.pi_iso OrderHom.piIso
#align order_hom.pi_iso_apply OrderHom.piIso_apply
#align order_hom.pi_iso_symm_apply OrderHom.piIso_symm_apply

/-- `Subtype.val` as a bundled monotone function.  -/
@[simps (config := { fullyApplied := false })]
def Subtype.val (p : α → Prop) : Subtype p →o α :=
  ⟨_root_.Subtype.val, fun _ _ h => h⟩
#align order_hom.subtype.val OrderHom.Subtype.val
#align order_hom.subtype.val_coe OrderHom.Subtype.val_coe

/-- There is a unique monotone map from a subsingleton to itself. -/
instance unique [Subsingleton α] : Unique (α →o α) where
  default := OrderHom.id
  uniq _ := ext _ _ (Subsingleton.elim _ _)
#align order_hom.unique OrderHom.unique

theorem orderHom_eq_id [Subsingleton α] (g : α →o α) : g = OrderHom.id :=
  Subsingleton.elim _ _
#align order_hom.order_hom_eq_id OrderHom.orderHom_eq_id

/-- Reinterpret a bundled monotone function as a monotone function between dual orders. -/
@[simps]
protected def dual : (α →o β) ≃ (αᵒᵈ →o βᵒᵈ) where
  toFun f := ⟨(OrderDual.toDual : β → βᵒᵈ) ∘ (f : α → β) ∘
    (OrderDual.ofDual : αᵒᵈ → α), f.mono.dual⟩
  invFun f := ⟨OrderDual.ofDual ∘ f ∘ OrderDual.toDual, f.mono.dual⟩
  left_inv _ := ext _ _ rfl
  right_inv _ := ext _ _ rfl
#align order_hom.dual OrderHom.dual
#align order_hom.dual_apply_coe OrderHom.dual_apply_coe
#align order_hom.dual_symm_apply_coe OrderHom.dual_symm_apply_coe

-- Porting note: We used to be able to write `(OrderHom.id : α →o α).dual` here rather than
-- `OrderHom.dual (OrderHom.id : α →o α)`.
-- See https://github.com/leanprover/lean4/issues/1910
@[simp]
theorem dual_id : OrderHom.dual (OrderHom.id : α →o α) = OrderHom.id :=
  rfl
#align order_hom.dual_id OrderHom.dual_id

@[simp]

@[simp]
theorem symm_dual_id : OrderHom.dual.symm OrderHom.id = (OrderHom.id : α →o α) :=
  rfl
#align order_hom.symm_dual_id OrderHom.symm_dual_id

@[simp]
theorem symm_dual_comp (g : βᵒᵈ →o γᵒᵈ) (f : αᵒᵈ →o βᵒᵈ) :
    OrderHom.dual.symm (g.comp f) = (OrderHom.dual.symm g).comp (OrderHom.dual.symm f) :=
  rfl
#align order_hom.symm_dual_comp OrderHom.symm_dual_comp

/-- `OrderHom.dual` as an order isomorphism. -/
def dualIso (α β : Type _) [Preorder α] [Preorder β] : (α →o β) ≃o (αᵒᵈ →o βᵒᵈ)ᵒᵈ where
  toEquiv := OrderHom.dual.trans OrderDual.toDual
  map_rel_iff' := Iff.rfl
#align order_hom.dual_iso OrderHom.dualIso

/-- Lift an order homomorphism `f : α →o β` to an order homomorphism `WithBot α →o WithBot β`. -/
@[simps (config := { fullyApplied := false })]
protected def withBotMap (f : α →o β) : WithBot α →o WithBot β :=
  ⟨WithBot.map f, f.mono.withBot_map⟩
#align order_hom.with_bot_map OrderHom.withBotMap
#align order_hom.with_bot_map_coe OrderHom.withBotMap_coe

/-- Lift an order homomorphism `f : α →o β` to an order homomorphism `WithTop α →o WithTop β`. -/
@[simps (config := { fullyApplied := false })]
protected def withTopMap (f : α →o β) : WithTop α →o WithTop β :=
  ⟨WithTop.map f, f.mono.withTop_map⟩
#align order_hom.with_top_map OrderHom.withTopMap
#align order_hom.with_top_map_coe OrderHom.withTopMap_coe

end OrderHom

/-- Embeddings of partial orders that preserve `<` also preserve `≤`. -/
def RelEmbedding.orderEmbeddingOfLTEmbedding [PartialOrder α] [PartialOrder β]
    (f : ((· < ·) : α → α → Prop) ↪r ((· < ·) : β → β → Prop)) : α ↪o β :=
  { f with
    map_rel_iff' := by
      intros
      simp [le_iff_lt_or_eq, f.map_rel_iff, f.injective.eq_iff] }
#align rel_embedding.order_embedding_of_lt_embedding RelEmbedding.orderEmbeddingOfLTEmbedding

@[simp]
theorem RelEmbedding.orderEmbeddingOfLTEmbedding_apply [PartialOrder α] [PartialOrder β]
    {f : ((· < ·) : α → α → Prop) ↪r ((· < ·) : β → β → Prop)} {x : α} :
    RelEmbedding.orderEmbeddingOfLTEmbedding f x = f x :=
  rfl
#align rel_embedding.order_embedding_of_lt_embedding_apply RelEmbedding.orderEmbeddingOfLTEmbedding_apply

namespace OrderEmbedding

variable [Preorder α] [Preorder β] (f : α ↪o β)

/-- `<` is preserved by order embeddings of preorders. -/
def ltEmbedding : ((· < ·) : α → α → Prop) ↪r ((· < ·) : β → β → Prop) :=
  { f with map_rel_iff' := by intros; simp [lt_iff_le_not_le, f.map_rel_iff] }
#align order_embedding.lt_embedding OrderEmbedding.ltEmbedding

@[simp]
theorem ltEmbedding_apply (x : α) : f.ltEmbedding x = f x :=
  rfl
#align order_embedding.lt_embedding_apply OrderEmbedding.ltEmbedding_apply

@[simp]
theorem le_iff_le {a b} : f a ≤ f b ↔ a ≤ b :=
  f.map_rel_iff
#align order_embedding.le_iff_le OrderEmbedding.le_iff_le

-- Porting note: `simp` can prove this.
-- @[simp]
theorem lt_iff_lt {a b} : f a < f b ↔ a < b :=
  f.ltEmbedding.map_rel_iff
#align order_embedding.lt_iff_lt OrderEmbedding.lt_iff_lt

-- Porting note: `simp` can prove this.
-- @[simp]
theorem eq_iff_eq {a b} : f a = f b ↔ a = b :=
  f.injective.eq_iff
#align order_embedding.eq_iff_eq OrderEmbedding.eq_iff_eq

protected theorem monotone : Monotone f :=
  OrderHomClass.monotone f
#align order_embedding.monotone OrderEmbedding.monotone

protected theorem strictMono : StrictMono f := fun _ _ => f.lt_iff_lt.2
#align order_embedding.strict_mono OrderEmbedding.strictMono

protected theorem acc (a : α) : Acc (· < ·) (f a) → Acc (· < ·) a :=
  f.ltEmbedding.acc a
#align order_embedding.acc OrderEmbedding.acc

protected theorem wellFounded :
    WellFounded ((· < ·) : β → β → Prop) → WellFounded ((· < ·) : α → α → Prop) :=
  f.ltEmbedding.wellFounded
#align order_embedding.well_founded OrderEmbedding.wellFounded

protected theorem isWellOrder [IsWellOrder β (· < ·)] : IsWellOrder α (· < ·) :=
  f.ltEmbedding.isWellOrder
#align order_embedding.is_well_order OrderEmbedding.isWellOrder

/-- An order embedding is also an order embedding between dual orders. -/
protected def dual : αᵒᵈ ↪o βᵒᵈ :=
  ⟨f.toEmbedding, f.map_rel_iff⟩
#align order_embedding.dual OrderEmbedding.dual

/-- A version of `WithBot.map` for order embeddings. -/
@[simps (config := { fullyApplied := false })]
protected def withBotMap (f : α ↪o β) : WithBot α ↪o WithBot β :=
  { f.toEmbedding.optionMap with
    toFun := WithBot.map f,
    map_rel_iff' := @fun a b => WithBot.map_le_iff f f.map_rel_iff a b }
#align order_embedding.with_bot_map OrderEmbedding.withBotMap
#align order_embedding.with_bot_map_apply OrderEmbedding.withBotMap_apply

/-- A version of `WithTop.map` for order embeddings. -/
@[simps (config := { fullyApplied := false })]
protected def withTopMap (f : α ↪o β) : WithTop α ↪o WithTop β :=
  { f.dual.withBotMap.dual with toFun := WithTop.map f }
#align order_embedding.with_top_map OrderEmbedding.withTopMap
#align order_embedding.with_top_map_apply OrderEmbedding.withTopMap_apply

/-- To define an order embedding from a partial order to a preorder it suffices to give a function
together with a proof that it satisfies `f a ≤ f b ↔ a ≤ b`.
-/
def ofMapLEIff {α β} [PartialOrder α] [Preorder β] (f : α → β) (hf : ∀ a b, f a ≤ f b ↔ a ≤ b) :
    α ↪o β :=
  RelEmbedding.ofMapRelIff f hf
#align order_embedding.of_map_le_iff OrderEmbedding.ofMapLEIff

@[simp]
theorem coe_ofMapLEIff {α β} [PartialOrder α] [Preorder β] {f : α → β} (h) :
    ⇑ofMapLEIff f h = f :=
  rfl
#align order_embedding.coe_of_map_le_iff OrderEmbedding.coe_ofMapLEIff

/-- A strictly monotone map from a linear order is an order embedding. -/
def ofStrictMono {α β} [LinearOrder α] [Preorder β] (f : α → β) (h : StrictMono f) : α ↪o β :=
  ofMapLEIff f fun _ _ => h.le_iff_le
#align order_embedding.of_strict_mono OrderEmbedding.ofStrictMono

@[simp]
theorem coe_ofStrictMono {α β} [LinearOrder α] [Preorder β] {f : α → β} (h : StrictMono f) :
    ⇑ofStrictMono f h = f :=
  rfl
#align order_embedding.coe_of_strict_mono OrderEmbedding.coe_ofStrictMono

/-- Embedding of a subtype into the ambient type as an `OrderEmbedding`. -/
@[simps! (config := { fullyApplied := false })]
def subtype (p : α → Prop) : Subtype p ↪o α :=
  ⟨Function.Embedding.subtype p, Iff.rfl⟩
#align order_embedding.subtype OrderEmbedding.subtype
#align order_embedding.subtype_apply OrderEmbedding.subtype_apply

/-- Convert an `OrderEmbedding` to a `OrderHom`. -/
@[simps (config := { fullyApplied := false })]
def toOrderHom {X Y : Type _} [Preorder X] [Preorder Y] (f : X ↪o Y) : X →o Y where
  toFun := f
  monotone' := f.monotone
#align order_embedding.to_order_hom OrderEmbedding.toOrderHom
#align order_embedding.to_order_hom_coe OrderEmbedding.toOrderHom_coe

end OrderEmbedding

section RelHom

variable [PartialOrder α] [Preorder β]

namespace RelHom

variable (f : ((· < ·) : α → α → Prop) →r ((· < ·) : β → β → Prop))

/-- A bundled expression of the fact that a map between partial orders that is strictly monotone
is weakly monotone. -/
@[simps (config := { fullyApplied := false })]
def toOrderHom : α →o β where
  toFun := f
  monotone' := StrictMono.monotone fun _ _ => f.map_rel
#align rel_hom.to_order_hom RelHom.toOrderHom
#align rel_hom.to_order_hom_coe RelHom.toOrderHom_coe

end RelHom

theorem RelEmbedding.toOrderHom_injective
    (f : ((· < ·) : α → α → Prop) ↪r ((· < ·) : β → β → Prop)) :
    Function.Injective (f : ((· < ·) : α → α → Prop) →r ((· < ·) : β → β → Prop)).toOrderHom :=
  fun _ _ h => f.injective h
#align rel_embedding.to_order_hom_injective RelEmbedding.toOrderHom_injective

end RelHom

namespace OrderIso

section LE

variable [LE α] [LE β] [LE γ]

instance : OrderIsoClass (α ≃o β) α β where
  coe f := f.toFun
  inv f := f.invFun
  left_inv f := f.left_inv
  right_inv f := f.right_inv
  coe_injective' f g h₁ h₂ := by
    obtain ⟨⟨_, _⟩, _⟩ := f
    obtain ⟨⟨_, _⟩, _⟩ := g
    congr
  map_le_map_iff f _ _ := f.map_rel_iff'

@[simp]
theorem toFun_eq_coe {f : α ≃o β} : f.toFun = f :=
  rfl
#align order_iso.to_fun_eq_coe OrderIso.toFun_eq_coe

-- See note [partially-applied ext lemmas]
@[ext]
theorem ext {f g : α ≃o β} (h : (f : α → β) = g) : f = g :=
  FunLike.coe_injective h
#align order_iso.ext OrderIso.ext

/-- Reinterpret an order isomorphism as an order embedding. -/
def toOrderEmbedding (e : α ≃o β) : α ↪o β :=
  e.toRelEmbedding
#align order_iso.to_order_embedding OrderIso.toOrderEmbedding

@[simp]
theorem coe_toOrderEmbedding (e : α ≃o β) : ⇑e.toOrderEmbedding = e :=
  rfl
#align order_iso.coe_to_order_embedding OrderIso.coe_toOrderEmbedding

protected theorem bijective (e : α ≃o β) : Function.Bijective e :=
  e.toEquiv.bijective
#align order_iso.bijective OrderIso.bijective

protected theorem injective (e : α ≃o β) : Function.Injective e :=
  e.toEquiv.injective
#align order_iso.injective OrderIso.injective

protected theorem surjective (e : α ≃o β) : Function.Surjective e :=
  e.toEquiv.surjective
#align order_iso.surjective OrderIso.surjective

-- Porting note: simp can prove this
-- @[simp]
theorem apply_eq_iff_eq (e : α ≃o β) {x y : α} : e x = e y ↔ x = y :=
  e.toEquiv.apply_eq_iff_eq
#align order_iso.apply_eq_iff_eq OrderIso.apply_eq_iff_eq

/-- Identity order isomorphism. -/
def refl (α : Type _) [LE α] : α ≃o α :=
  RelIso.refl (· ≤ ·)
#align order_iso.refl OrderIso.refl

@[simp]
theorem coe_refl : ⇑refl α = id :=
  rfl
#align order_iso.coe_refl OrderIso.coe_refl

@[simp]
theorem refl_apply (x : α) : refl α x = x :=
  rfl
#align order_iso.refl_apply OrderIso.refl_apply

@[simp]
theorem refl_toEquiv : (refl α).toEquiv = Equiv.refl α :=
  rfl
#align order_iso.refl_to_equiv OrderIso.refl_toEquiv

/-- Inverse of an order isomorphism. -/
def symm (e : α ≃o β) : β ≃o α := RelIso.symm e
#align order_iso.symm OrderIso.symm

@[simp]
theorem apply_symm_apply (e : α ≃o β) (x : β) : e (e.symm x) = x :=
  e.toEquiv.apply_symm_apply x
#align order_iso.apply_symm_apply OrderIso.apply_symm_apply

@[simp]
theorem symm_apply_apply (e : α ≃o β) (x : α) : e.symm (e x) = x :=
  e.toEquiv.symm_apply_apply x
#align order_iso.symm_apply_apply OrderIso.symm_apply_apply

@[simp]
theorem symm_refl (α : Type _) [LE α] : (refl α).symm = refl α :=
  rfl
#align order_iso.symm_refl OrderIso.symm_refl

theorem apply_eq_iff_eq_symm_apply (e : α ≃o β) (x : α) (y : β) : e x = y ↔ x = e.symm y :=
  e.toEquiv.apply_eq_iff_eq_symm_apply
#align order_iso.apply_eq_iff_eq_symm_apply OrderIso.apply_eq_iff_eq_symm_apply

theorem symm_apply_eq (e : α ≃o β) {x : α} {y : β} : e.symm y = x ↔ y = e x :=
  e.toEquiv.symm_apply_eq
#align order_iso.symm_apply_eq OrderIso.symm_apply_eq

@[simp]
theorem symm_symm (e : α ≃o β) : e.symm.symm = e := by
  ext
  rfl
#align order_iso.symm_symm OrderIso.symm_symm

theorem symm_injective : Function.Injective (symm : α ≃o β → β ≃o α) := fun e e' h => by
  rw [← e.symm_symm, h, e'.symm_symm]
#align order_iso.symm_injective OrderIso.symm_injective

@[simp]
theorem toEquiv_symm (e : α ≃o β) : e.toEquiv.symm = e.symm.toEquiv :=
  rfl
#align order_iso.to_equiv_symm OrderIso.toEquiv_symm

/-- Composition of two order isomorphisms is an order isomorphism. -/
@[trans]
def trans (e : α ≃o β) (e' : β ≃o γ) : α ≃o γ :=
  RelIso.trans e e'
#align order_iso.trans OrderIso.trans

@[simp]
theorem coe_trans (e : α ≃o β) (e' : β ≃o γ) : ⇑e.trans e' = e' ∘ e :=
  rfl
#align order_iso.coe_trans OrderIso.coe_trans

@[simp]
theorem trans_apply (e : α ≃o β) (e' : β ≃o γ) (x : α) : e.trans e' x = e' (e x) :=
  rfl
#align order_iso.trans_apply OrderIso.trans_apply

@[simp]
theorem refl_trans (e : α ≃o β) : (refl α).trans e = e := by
  ext x
  rfl
#align order_iso.refl_trans OrderIso.refl_trans

@[simp]
theorem trans_refl (e : α ≃o β) : e.trans (refl β) = e := by
  ext x
  rfl
#align order_iso.trans_refl OrderIso.trans_refl

@[simp]
theorem symm_trans_apply (e₁ : α ≃o β) (e₂ : β ≃o γ) (c : γ) :
    (e₁.trans e₂).symm c = e₁.symm (e₂.symm c) :=
  rfl
#align order_iso.symm_trans_apply OrderIso.symm_trans_apply

theorem symm_trans (e₁ : α ≃o β) (e₂ : β ≃o γ) : (e₁.trans e₂).symm = e₂.symm.trans e₁.symm :=
  rfl
#align order_iso.symm_trans OrderIso.symm_trans

/-- `Prod.swap` as an `OrderIso`. -/
def prodComm : α × β ≃o β × α where
  toEquiv := Equiv.prodComm α β
  map_rel_iff' := Prod.swap_le_swap
#align order_iso.prod_comm OrderIso.prodComm

@[simp]
theorem coe_prodComm : ⇑(prodComm : α × β ≃o β × α) = Prod.swap :=
  rfl
#align order_iso.coe_prod_comm OrderIso.coe_prodComm

@[simp]
theorem prodComm_symm : (prodComm : α × β ≃o β × α).symm = prodComm :=
  rfl
#align order_iso.prod_comm_symm OrderIso.prodComm_symm

variable (α)

/-- The order isomorphism between a type and its double dual. -/
def dualDual : α ≃o αᵒᵈᵒᵈ :=
  refl α
#align order_iso.dual_dual OrderIso.dualDual

@[simp]
theorem coe_dualDual : ⇑dualDual α = toDual ∘ toDual :=
  rfl
#align order_iso.coe_dual_dual OrderIso.coe_dualDual

@[simp]
theorem coe_dualDual_symm : ⇑(dualDual α).symm = ofDual ∘ ofDual :=
  rfl
#align order_iso.coe_dual_dual_symm OrderIso.coe_dualDual_symm

variable {α}

@[simp]
theorem dualDual_apply (a : α) : dualDual α a = toDual (toDual a) :=
  rfl
#align order_iso.dual_dual_apply OrderIso.dualDual_apply

@[simp]
theorem dualDual_symm_apply (a : αᵒᵈᵒᵈ) : (dualDual α).symm a = ofDual (ofDual a) :=
  rfl
#align order_iso.dual_dual_symm_apply OrderIso.dualDual_symm_apply

end LE

open Set

section LE

variable [LE α] [LE β] [LE γ]

@[simp]
theorem le_iff_le (e : α ≃o β) {x y : α} : e x ≤ e y ↔ x ≤ y :=
  e.map_rel_iff
#align order_iso.le_iff_le OrderIso.le_iff_le

theorem le_symm_apply (e : α ≃o β) {x : α} {y : β} : x ≤ e.symm y ↔ e x ≤ y :=
  e.rel_symm_apply
#align order_iso.le_symm_apply OrderIso.le_symm_apply

theorem symm_apply_le (e : α ≃o β) {x : α} {y : β} : e.symm y ≤ x ↔ y ≤ e x :=
  e.symm_apply_rel
#align order_iso.symm_apply_le OrderIso.symm_apply_le

end LE

variable [Preorder α] [Preorder β] [Preorder γ]

protected theorem monotone (e : α ≃o β) : Monotone e :=
  e.toOrderEmbedding.monotone
#align order_iso.monotone OrderIso.monotone

protected theorem strictMono (e : α ≃o β) : StrictMono e :=
  e.toOrderEmbedding.strictMono
#align order_iso.strict_mono OrderIso.strictMono

@[simp]
theorem lt_iff_lt (e : α ≃o β) {x y : α} : e x < e y ↔ x < y :=
  e.toOrderEmbedding.lt_iff_lt
#align order_iso.lt_iff_lt OrderIso.lt_iff_lt

/-- Converts an `OrderIso` into a `RelIso (<) (<)`. -/
def toRelIsoLT (e : α ≃o β) : ((· < ·) : α → α → Prop) ≃r ((· < ·) : β → β → Prop) :=
  ⟨e.toEquiv, lt_iff_lt e⟩
#align order_iso.to_rel_iso_lt OrderIso.toRelIsoLT

@[simp]
theorem toRelIsoLT_apply (e : α ≃o β) (x : α) : e.toRelIsoLT x = e x :=
  rfl
#align order_iso.to_rel_iso_lt_apply OrderIso.toRelIsoLT_apply

@[simp]
theorem toRelIsoLT_symm (e : α ≃o β) : e.toRelIsoLT.symm = e.symm.toRelIsoLT :=
  rfl
#align order_iso.to_rel_iso_lt_symm OrderIso.toRelIsoLT_symm

/-- Converts a `RelIso (<) (<)` into an `OrderIso`. -/
def ofRelIsoLT {α β} [PartialOrder α] [PartialOrder β]
    (e : ((· < ·) : α → α → Prop) ≃r ((· < ·) : β → β → Prop)) : α ≃o β :=
  ⟨e.toEquiv, by simp [le_iff_eq_or_lt, e.map_rel_iff]⟩
#align order_iso.of_rel_iso_lt OrderIso.ofRelIsoLT

@[simp]
theorem ofRelIsoLT_apply {α β} [PartialOrder α] [PartialOrder β]
    (e : ((· < ·) : α → α → Prop) ≃r ((· < ·) : β → β → Prop)) (x : α) : ofRelIsoLT e x = e x :=
  rfl
#align order_iso.of_rel_iso_lt_apply OrderIso.ofRelIsoLT_apply

@[simp]
theorem ofRelIsoLT_symm {α β} [PartialOrder α] [PartialOrder β]
    (e : ((· < ·) : α → α → Prop) ≃r ((· < ·) : β → β → Prop)) :
    (ofRelIsoLT e).symm = ofRelIsoLT e.symm :=
  rfl
#align order_iso.of_rel_iso_lt_symm OrderIso.ofRelIsoLT_symm

@[simp]
theorem ofRelIsoLT_toRelIsoLT {α β} [PartialOrder α] [PartialOrder β] (e : α ≃o β) :
    ofRelIsoLT (toRelIsoLT e) = e := by
  ext
  simp
#align order_iso.of_rel_iso_lt_to_rel_iso_lt OrderIso.ofRelIsoLT_toRelIsoLT

@[simp]
theorem toRelIsoLT_ofRelIsoLT {α β} [PartialOrder α] [PartialOrder β]
    (e : ((· < ·) : α → α → Prop) ≃r ((· < ·) : β → β → Prop)) : toRelIsoLT (ofRelIsoLT e) = e := by
  ext
  simp
#align order_iso.to_rel_iso_lt_of_rel_iso_lt OrderIso.toRelIsoLT_ofRelIsoLT

/-- To show that `f : α → β`, `g : β → α` make up an order isomorphism of linear orders,
    it suffices to prove `cmp a (g b) = cmp (f a) b`. -/
def ofCmpEqCmp {α β} [LinearOrder α] [LinearOrder β] (f : α → β) (g : β → α)
    (h : ∀ (a : α) (b : β), cmp a (g b) = cmp (f a) b) : α ≃o β :=
  have gf : ∀ a : α, a = g (f a) := by
    intro
    rw [← cmp_eq_eq_iff, h, cmp_self_eq_eq]
  { toFun := f, invFun := g, left_inv := fun a => (gf a).symm,
    right_inv := by
      intro
      rw [← cmp_eq_eq_iff, ← h, cmp_self_eq_eq],
    map_rel_iff' := by
      intros a b
      apply le_iff_le_of_cmp_eq_cmp
      convert (h a (f b)).symm
      apply gf }
#align order_iso.of_cmp_eq_cmp OrderIso.ofCmpEqCmp

/-- To show that `f : α →o β` and `g : β →o α` make up an order isomorphism it is enough to show
    that `g` is the inverse of `f`-/
def ofHomInv {F G : Type _} [OrderHomClass F α β] [OrderHomClass G β α] (f : F) (g : G)
    (h₁ : (f : α →o β).comp (g : β →o α) = OrderHom.id)
    (h₂ : (g : β →o α).comp (f : α →o β) = OrderHom.id) :
    α ≃o β where
  toFun := f
  invFun := g
  left_inv := FunLike.congr_fun h₂
  right_inv := FunLike.congr_fun h₁
  map_rel_iff' := @fun a b =>
    ⟨fun h => by
      replace h := map_rel g h
      rwa [Equiv.coe_fn_mk, show g (f a) = (g : β →o α).comp (f : α →o β) a from rfl,
        show g (f b) = (g : β →o α).comp (f : α →o β) b from rfl, h₂] at h,
      fun h => (f : α →o β).monotone h⟩
#align order_iso.of_hom_inv OrderIso.ofHomInv

/-- Order isomorphism between `α → β` and `β`, where `α` has a unique element. -/
@[simps! toEquiv apply]
def funUnique (α β : Type _) [Unique α] [Preorder β] : (α → β) ≃o β where
  toEquiv := Equiv.funUnique α β
  map_rel_iff' := by simp [Pi.le_def, Unique.forall_iff]
#align order_iso.fun_unique OrderIso.funUnique
#align order_iso.fun_unique_apply OrderIso.funUnique_apply
#align order_iso.fun_unique_to_equiv OrderIso.funUnique_toEquiv

@[simp]
theorem funUnique_symm_apply {α β : Type _} [Unique α] [Preorder β] :
    ((funUnique α β).symm : β → α → β) = Function.const α :=
  rfl
#align order_iso.fun_unique_symm_apply OrderIso.funUnique_symm_apply

end OrderIso

namespace Equiv

variable [Preorder α] [Preorder β]

/-- If `e` is an equivalence with monotone forward and inverse maps, then `e` is an
order isomorphism. -/
def toOrderIso (e : α ≃ β) (h₁ : Monotone e) (h₂ : Monotone e.symm) : α ≃o β :=
  ⟨e, ⟨fun h => by simpa only [e.symm_apply_apply] using h₂ h, fun h => h₁ h⟩⟩
#align equiv.to_order_iso Equiv.toOrderIso

@[simp]
theorem coe_toOrderIso (e : α ≃ β) (h₁ : Monotone e) (h₂ : Monotone e.symm) :
    ⇑e.toOrderIso h₁ h₂ = e :=
  rfl
#align equiv.coe_to_order_iso Equiv.coe_toOrderIso

@[simp]
theorem toOrderIso_toEquiv (e : α ≃ β) (h₁ : Monotone e) (h₂ : Monotone e.symm) :
    (e.toOrderIso h₁ h₂).toEquiv = e :=
  rfl
#align equiv.to_order_iso_to_equiv Equiv.toOrderIso_toEquiv

end Equiv

namespace StrictMono

variable [LinearOrder α] [Preorder β]

variable (f : α → β) (h_mono : StrictMono f) (h_surj : Function.Surjective f)

/-- A strictly monotone function with a right inverse is an order isomorphism. -/
@[simps (config := { fullyApplied := False })]
def orderIsoOfRightInverse (g : β → α) (hg : Function.RightInverse g f) : α ≃o β :=
  { OrderEmbedding.ofStrictMono f h_mono with
    toFun := f,
    invFun := g,
    left_inv := fun _ => h_mono.injective <| hg _,
    right_inv := hg }
#align strict_mono.order_iso_of_right_inverse StrictMono.orderIsoOfRightInverse
#align strict_mono.order_iso_of_right_inverse_apply StrictMono.orderIsoOfRightInverse_apply
#align strict_mono.order_iso_of_right_inverse_symm_apply StrictMono.orderIsoOfRightInverse_symm_apply

end StrictMono

/-- An order isomorphism is also an order isomorphism between dual orders. -/
protected def OrderIso.dual [LE α] [LE β] (f : α ≃o β) : αᵒᵈ ≃o βᵒᵈ :=
  ⟨f.toEquiv, f.map_rel_iff⟩
#align order_iso.dual OrderIso.dual

section LatticeIsos

theorem OrderIso.map_bot' [LE α] [PartialOrder β] (f : α ≃o β) {x : α} {y : β} (hx : ∀ x', x ≤ x')
    (hy : ∀ y', y ≤ y') : f x = y := by
  refine' le_antisymm _ (hy _)
  rw [← f.apply_symm_apply y, f.map_rel_iff]
  apply hx
#align order_iso.map_bot' OrderIso.map_bot'

theorem OrderIso.map_bot [LE α] [PartialOrder β] [OrderBot α] [OrderBot β] (f : α ≃o β) : f ⊥ = ⊥ :=
  f.map_bot' (fun _ => bot_le) fun _ => bot_le
#align order_iso.map_bot OrderIso.map_bot

theorem OrderIso.map_top' [LE α] [PartialOrder β] (f : α ≃o β) {x : α} {y : β} (hx : ∀ x', x' ≤ x)
    (hy : ∀ y', y' ≤ y) : f x = y :=
  f.dual.map_bot' hx hy
#align order_iso.map_top' OrderIso.map_top'

theorem OrderIso.map_top [LE α] [PartialOrder β] [OrderTop α] [OrderTop β] (f : α ≃o β) : f ⊤ = ⊤ :=
  f.dual.map_bot
#align order_iso.map_top OrderIso.map_top

theorem OrderEmbedding.map_inf_le [SemilatticeInf α] [SemilatticeInf β] (f : α ↪o β) (x y : α) :
    f (x ⊓ y) ≤ f x ⊓ f y :=
  f.monotone.map_inf_le x y
#align order_embedding.map_inf_le OrderEmbedding.map_inf_le

theorem OrderEmbedding.le_map_sup [SemilatticeSup α] [SemilatticeSup β] (f : α ↪o β) (x y : α) :
    f x ⊔ f y ≤ f (x ⊔ y) :=
  f.monotone.le_map_sup x y
#align order_embedding.le_map_sup OrderEmbedding.le_map_sup

theorem OrderIso.map_inf [SemilatticeInf α] [SemilatticeInf β] (f : α ≃o β) (x y : α) :
    f (x ⊓ y) = f x ⊓ f y := by
  refine' (f.toOrderEmbedding.map_inf_le x y).antisymm _
  apply f.symm.le_iff_le.1
  simpa using f.symm.toOrderEmbedding.map_inf_le (f x) (f y)
#align order_iso.map_inf OrderIso.map_inf

theorem OrderIso.map_sup [SemilatticeSup α] [SemilatticeSup β] (f : α ≃o β) (x y : α) :
    f (x ⊔ y) = f x ⊔ f y :=
  f.dual.map_inf x y
#align order_iso.map_sup OrderIso.map_sup

/-- Note that this goal could also be stated `(Disjoint on f) a b` -/
theorem Disjoint.map_orderIso [SemilatticeInf α] [OrderBot α] [SemilatticeInf β] [OrderBot β]
    {a b : α} (f : α ≃o β) (ha : Disjoint a b) : Disjoint (f a) (f b) := by
  rw [disjoint_iff_inf_le, ← f.map_inf, ← f.map_bot]
  exact f.monotone ha.le_bot
#align disjoint.map_order_iso Disjoint.map_orderIso

/-- Note that this goal could also be stated `(Codisjoint on f) a b` -/
theorem Codisjoint.map_orderIso [SemilatticeSup α] [OrderTop α] [SemilatticeSup β] [OrderTop β]
    {a b : α} (f : α ≃o β) (ha : Codisjoint a b) : Codisjoint (f a) (f b) := by
  rw [codisjoint_iff_le_sup, ← f.map_sup, ← f.map_top]
  exact f.monotone ha.top_le
#align codisjoint.map_order_iso Codisjoint.map_orderIso

@[simp]
theorem disjoint_map_orderIso_iff [SemilatticeInf α] [OrderBot α] [SemilatticeInf β] [OrderBot β]
    {a b : α} (f : α ≃o β) : Disjoint (f a) (f b) ↔ Disjoint a b :=
  ⟨fun h => f.symm_apply_apply a ▸ f.symm_apply_apply b ▸ h.map_orderIso f.symm,
   fun h => h.map_orderIso f⟩
#align disjoint_map_order_iso_iff disjoint_map_orderIso_iff

@[simp]
theorem codisjoint_map_orderIso_iff [SemilatticeSup α] [OrderTop α] [SemilatticeSup β] [OrderTop β]
    {a b : α} (f : α ≃o β) : Codisjoint (f a) (f b) ↔ Codisjoint a b :=
  ⟨fun h => f.symm_apply_apply a ▸ f.symm_apply_apply b ▸ h.map_orderIso f.symm,
   fun h => h.map_orderIso f⟩
#align codisjoint_map_order_iso_iff codisjoint_map_orderIso_iff

namespace WithBot

/-- Taking the dual then adding `⊥` is the same as adding `⊤` then taking the dual.
This is the order iso form of `WithBot.ofDual`, as proven by `coe_toDualTopEquiv_eq`.
-/
protected def toDualTopEquiv [LE α] : WithBot αᵒᵈ ≃o (WithTop α)ᵒᵈ :=
  OrderIso.refl _
#align with_bot.to_dual_top_equiv WithBot.toDualTopEquiv

@[simp]
theorem toDualTopEquiv_coe [LE α] (a : α) :
    WithBot.toDualTopEquiv ↑(toDual a) = toDual (a : WithTop α) :=
  rfl
#align with_bot.to_dual_top_equiv_coe WithBot.toDualTopEquiv_coe

@[simp]
theorem toDualTopEquiv_symm_coe [LE α] (a : α) :
    WithBot.toDualTopEquiv.symm (toDual (a : WithTop α)) = ↑(toDual a) :=
  rfl
#align with_bot.to_dual_top_equiv_symm_coe WithBot.toDualTopEquiv_symm_coe

@[simp]
theorem toDualTopEquiv_bot [LE α] : WithBot.toDualTopEquiv (⊥ : WithBot αᵒᵈ) = ⊥ :=
  rfl
#align with_bot.to_dual_top_equiv_bot WithBot.toDualTopEquiv_bot

@[simp]
theorem toDualTopEquiv_symm_bot [LE α] : WithBot.toDualTopEquiv.symm (⊥ : (WithTop α)ᵒᵈ) = ⊥ :=
  rfl
#align with_bot.to_dual_top_equiv_symm_bot WithBot.toDualTopEquiv_symm_bot

theorem coe_toDualTopEquiv_eq [LE α] :
    (WithBot.toDualTopEquiv : WithBot αᵒᵈ → (WithTop α)ᵒᵈ) = toDual ∘ WithBot.ofDual :=
  funext fun _ => rfl
#align with_bot.coe_to_dual_top_equiv_eq WithBot.coe_toDualTopEquiv_eq

end WithBot

namespace WithTop

/-- Taking the dual then adding `⊤` is the same as adding `⊥` then taking the dual.
This is the order iso form of `WithTop.ofDual`, as proven by `coe_toDualBotEquiv_eq`. -/
protected def toDualBotEquiv [LE α] : WithTop αᵒᵈ ≃o (WithBot α)ᵒᵈ :=
  OrderIso.refl _
#align with_top.to_dual_bot_equiv WithTop.toDualBotEquiv

@[simp]
theorem toDualBotEquiv_coe [LE α] (a : α) :
    WithTop.toDualBotEquiv ↑(toDual a) = toDual (a : WithBot α) :=
  rfl
#align with_top.to_dual_bot_equiv_coe WithTop.toDualBotEquiv_coe

@[simp]
theorem toDualBotEquiv_symm_coe [LE α] (a : α) :
    WithTop.toDualBotEquiv.symm (toDual (a : WithBot α)) = ↑(toDual a) :=
  rfl
#align with_top.to_dual_bot_equiv_symm_coe WithTop.toDualBotEquiv_symm_coe

@[simp]
theorem toDualBotEquiv_top [LE α] : WithTop.toDualBotEquiv (⊤ : WithTop αᵒᵈ) = ⊤ :=
  rfl
#align with_top.to_dual_bot_equiv_top WithTop.toDualBotEquiv_top

@[simp]
theorem toDualBotEquiv_symm_top [LE α] : WithTop.toDualBotEquiv.symm (⊤ : (WithBot α)ᵒᵈ) = ⊤ :=
  rfl
#align with_top.to_dual_bot_equiv_symm_top WithTop.toDualBotEquiv_symm_top

theorem coe_toDualBotEquiv [LE α] :
    (WithTop.toDualBotEquiv : WithTop αᵒᵈ → (WithBot α)ᵒᵈ) = toDual ∘ WithTop.ofDual :=
  funext fun _ => rfl
#align with_top.coe_to_dual_bot_equiv_eq WithTop.coe_toDualBotEquiv

end WithTop

namespace OrderIso

variable [PartialOrder α] [PartialOrder β] [PartialOrder γ]

/-- A version of `Equiv.optionCongr` for `WithTop`. -/
@[simps! apply]
def withTopCongr (e : α ≃o β) : WithTop α ≃o WithTop β :=
  { e.toOrderEmbedding.withTopMap with
    toEquiv := e.toEquiv.optionCongr }
#align order_iso.with_top_congr OrderIso.withTopCongr
#align order_iso.with_top_congr_apply OrderIso.withTopCongr_apply

@[simp]
theorem withTopCongr_refl : (OrderIso.refl α).withTopCongr = OrderIso.refl _ :=
  RelIso.toEquiv_injective Equiv.optionCongr_refl
#align order_iso.with_top_congr_refl OrderIso.withTopCongr_refl

@[simp]
theorem withTopCongr_symm (e : α ≃o β) : e.withTopCongr.symm = e.symm.withTopCongr :=
  RelIso.toEquiv_injective e.toEquiv.optionCongr_symm
#align order_iso.with_top_congr_symm OrderIso.withTopCongr_symm

@[simp]
theorem withTopCongr_trans (e₁ : α ≃o β) (e₂ : β ≃o γ) :
    e₁.withTopCongr.trans e₂.withTopCongr = (e₁.trans e₂).withTopCongr :=
  RelIso.toEquiv_injective <| e₁.toEquiv.optionCongr_trans e₂.toEquiv
#align order_iso.with_top_congr_trans OrderIso.withTopCongr_trans

/-- A version of `Equiv.optionCongr` for `WithBot`. -/
@[simps! apply]
def withBotCongr (e : α ≃o β) : WithBot α ≃o WithBot β :=
  { e.toOrderEmbedding.withBotMap with toEquiv := e.toEquiv.optionCongr }
#align order_iso.with_bot_congr OrderIso.withBotCongr
#align order_iso.with_bot_congr_apply OrderIso.withBotCongr_apply

@[simp]
theorem withBotCongr_refl : (OrderIso.refl α).withBotCongr = OrderIso.refl _ :=
  RelIso.toEquiv_injective Equiv.optionCongr_refl
#align order_iso.with_bot_congr_refl OrderIso.withBotCongr_refl

@[simp]
theorem withBotCongr_symm (e : α ≃o β) : e.withBotCongr.symm = e.symm.withBotCongr :=
  RelIso.toEquiv_injective e.toEquiv.optionCongr_symm
#align order_iso.with_bot_congr_symm OrderIso.withBotCongr_symm

@[simp]
theorem withBotCongr_trans (e₁ : α ≃o β) (e₂ : β ≃o γ) :
    e₁.withBotCongr.trans e₂.withBotCongr = (e₁.trans e₂).withBotCongr :=
  RelIso.toEquiv_injective <| e₁.toEquiv.optionCongr_trans e₂.toEquiv
#align order_iso.with_bot_congr_trans OrderIso.withBotCongr_trans

end OrderIso

section BoundedOrder

variable [Lattice α] [Lattice β] [BoundedOrder α] [BoundedOrder β] (f : α ≃o β)

theorem OrderIso.isCompl {x y : α} (h : IsCompl x y) : IsCompl (f x) (f y) :=
  ⟨h.1.map_orderIso _, h.2.map_orderIso _⟩
#align order_iso.is_compl OrderIso.isCompl

theorem OrderIso.isCompl_iff {x y : α} : IsCompl x y ↔ IsCompl (f x) (f y) :=
  ⟨f.isCompl, fun h => f.symm_apply_apply x ▸ f.symm_apply_apply y ▸ f.symm.isCompl h⟩
#align order_iso.is_compl_iff OrderIso.isCompl_iff

theorem OrderIso.complementedLattice [ComplementedLattice α] : ComplementedLattice β :=
  ⟨fun x => by
    obtain ⟨y, hy⟩ := exists_isCompl (f.symm x)
    rw [← f.symm_apply_apply y] at hy
    exact ⟨f y, f.symm.isCompl_iff.2 hy⟩⟩
#align order_iso.complemented_lattice OrderIso.complementedLattice

theorem OrderIso.complementedLattice_iff : ComplementedLattice α ↔ ComplementedLattice β :=
  ⟨by intro; exact f.complementedLattice,
   by intro; exact f.symm.complementedLattice⟩
#align order_iso.complemented_lattice_iff OrderIso.complementedLattice_iff

end BoundedOrder

end LatticeIsos

-- Developments relating order homs and sets belong in `order.hom.set` or later.
-- porting note: command not ported yet (added in mathlib#17416)
-- assert_not_exists set.range


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

! This file was ported from Lean 3 source module algebra.category.Group.injective
! 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.Algebra.Category.Group.EpiMono
import Mathbin.Algebra.Category.Module.EpiMono
import Mathbin.Algebra.Module.Injective
import Mathbin.CategoryTheory.Preadditive.Injective
import Mathbin.GroupTheory.Divisible
import Mathbin.RingTheory.PrincipalIdealDomain

/-!
# Injective objects in the category of abelian groups

In this file we prove that divisible groups are injective object in category of (additive) abelian
groups.

-/

open CategoryTheory

open Pointwise

universe u

variable (A : Type u) [AddCommGroup A]

namespace AddCommGroupCat

theorem injective_of_injective_as_module [Injective (⟨A⟩ : ModuleCat ℤ)] :
    CategoryTheory.Injective (⟨A⟩ : AddCommGroupCat) :=
  {
    Factors := fun X Y g f m => by
      skip
      let G : (⟨X⟩ : ModuleCat ℤ) ⟶ ⟨A⟩ :=
        { g with
          map_smul' := by
            intros
            rw [RingHom.id_apply, g.to_fun_eq_coe, map_zsmul] }
      let F : (⟨X⟩ : ModuleCat ℤ) ⟶ ⟨Y⟩ :=
        { f with
          map_smul' := by
            intros
            rw [RingHom.id_apply, f.to_fun_eq_coe, map_zsmul] }
      have : mono F := by
        refine' ⟨fun Z α β eq1 => _⟩
        let α' : AddCommGroupCat.of Z ⟶ X := α.to_add_monoid_hom
        let β' : AddCommGroupCat.of Z ⟶ X := β.to_add_monoid_hom
        have eq2 : α' ≫ f = β' ≫ f := by
          ext
          simp only [CategoryTheory.comp_apply, LinearMap.toAddMonoidHom_coe]
          simpa only [ModuleCat.coe_comp, LinearMap.coe_mk, Function.comp_apply] using
            FunLike.congr_fun eq1 x
        rw [cancel_mono] at eq2
        ext
        simpa only using FunLike.congr_fun eq2 x
      refine' ⟨(injective.factor_thru G F).toAddMonoidHom, _⟩
      ext
      convert FunLike.congr_fun (injective.comp_factor_thru G F) x }
#align AddCommGroup.injective_of_injective_as_module AddCommGroupCat.injective_of_injective_as_module

theorem injective_as_module_of_injective_as_Ab [Injective (⟨A⟩ : AddCommGroupCat)] :
    Injective (⟨A⟩ : ModuleCat ℤ) :=
  {
    Factors := fun X Y g f m => by
      skip
      let G : (⟨X⟩ : AddCommGroupCat) ⟶ ⟨A⟩ := g.to_add_monoid_hom
      let F : (⟨X⟩ : AddCommGroupCat) ⟶ ⟨Y⟩ := f.to_add_monoid_hom
      have : mono F := by
        rw [mono_iff_injective]
        intro _ _ h
        exact ((ModuleCat.mono_iff_injective f).mp m) h
      refine' ⟨{ injective.factor_thru G F with map_smul' := _ }, _⟩
      · intro m x
        rw [AddMonoidHom.toFun_eq_coe, RingHom.id_apply]
        induction' m using Int.induction_on with n hn n hn
        · rw [zero_smul]
          convert map_zero _
          convert zero_smul _ x
        · simp only [add_smul, map_add, hn, one_smul]
        · simp only [sub_smul, map_sub, hn, one_smul]
      ext
      convert FunLike.congr_fun (injective.comp_factor_thru G F) x }
#align AddCommGroup.injective_as_module_of_injective_as_Ab AddCommGroupCat.injective_as_module_of_injective_as_Ab

instance injective_of_divisible [DivisibleBy A ℤ] :
    CategoryTheory.Injective (⟨A⟩ : AddCommGroupCat) :=
  @injective_of_injective_as_module A _ <|
    @Module.injective_object_of_injective_module ℤ _ A _ _ <|
      Module.Baer.injective fun I g =>
        by
        rcases IsPrincipalIdealRing.principal I with ⟨m, rfl⟩
        by_cases m_eq_zero : m = 0
        · subst m_eq_zero
          refine'
            ⟨{  toFun := _
                map_add' := _
                map_smul' := _ }, fun n hn => _⟩
          · intro n
            exact g 0
          · intro n1 n2
            simp only [map_zero, add_zero]
          · intro n1 n2
            simp only [map_zero, smul_zero]
          · rw [submodule.span_singleton_eq_bot.mpr rfl, Submodule.mem_bot] at hn
            simp only [hn, map_zero]
            symm
            convert map_zero _
        · set gₘ := g ⟨m, Submodule.subset_span (Set.mem_singleton _)⟩ with gm_eq
          refine'
            ⟨{  toFun := _
                map_add' := _
                map_smul' := _ }, fun n hn => _⟩
          · intro n
            exact n • DivisibleBy.div gₘ m
          · intro n1 n2
            simp only [add_smul]
          · intro n1 n2
            rw [RingHom.id_apply, smul_eq_mul, mul_smul]
          · rw [Submodule.mem_span_singleton] at hn
            rcases hn with ⟨n, rfl⟩
            simp only [gm_eq, Algebra.id.smul_eq_mul, LinearMap.coe_mk]
            rw [mul_smul, DivisibleBy.div_cancel (g ⟨m, _⟩) m_eq_zero, ← LinearMap.map_smul]
            congr
#align AddCommGroup.injective_of_divisible AddCommGroupCat.injective_of_divisible

end AddCommGroupCat


import data.finset

import lib.core.rel3

variable {α : Type*}

-- Local notion for a list contained in a finset
protected def list.in (l : list α) (S : finset α) : Prop :=
  ∀ a : α, a ∈ l → a ∈ S

protected def finset.mirror
  [linear_order α] (S : finset α) : finset αᵒᵈ :=
  (finset.image order_dual.to_dual S)

protected def finset.of_mirror
  [linear_order α] (S : finset αᵒᵈ) : finset α :=
  (finset.image order_dual.of_dual S)

namespace list

-- Local notion for flipping a list of elements, together with its order
protected def mirror
  [linear_order α] (l : list α) : list αᵒᵈ :=
  (list.map order_dual.to_dual l).reverse

protected def of_mirror
  [linear_order α] (l : list αᵒᵈ) : list α :=
  (list.map order_dual.of_dual l).reverse

variable (R : α → α → α → Prop)

inductive chain3 : α → α → list α → Prop
| nil {a b : α} : chain3 a b []
| cons : ∀ {a b c : α} {l : list α},
    R a b c → chain3 b c l → chain3 a b (c :: l)

def chain3' : list α → Prop
| [] := true
| [_] := true
| (a :: b :: l) := chain3 R a b l

variable {R}
@[simp]
theorem chain3_cons {a b c : α} {l : list α} :
  chain3 R a b (c :: l) ↔ R a b c ∧ chain3 R b c l :=
⟨ λ p, by cases p with _ a b c l _ n p; exact ⟨n, p⟩,
  λ ⟨n, p⟩, p.cons n ⟩

attribute [simp] chain3.nil

instance decidable_chain3 [decidable_rel3 R] (a b : α) (l : list α) :
  decidable (chain3 R a b l) :=
by induction l generalizing a b;
  simp only [chain3.nil, chain3_cons];
  resetI; apply_instance

instance decidable_chain3' [decidable_rel3 R] (l : list α) :
  decidable (chain3' R l) :=
by cases l with _ l; try {cases l with _ l};
  dunfold chain3'; apply_instance

end list


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro

! This file was ported from Lean 3 source module data.rat.order
! leanprover-community/mathlib commit a59dad53320b73ef180174aae867addd707ef00e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Order.Field.Defs
import Mathlib.Data.Rat.Basic
import Mathlib.Data.Int.Cast.Lemmas

/-!
# Order for Rational Numbers

## Summary

We define the order on `ℚ`, prove that `ℚ` is a discrete, linearly ordered field, and define
functions such as `abs` and `sqrt` that depend on this order.

## Tags

rat, rationals, field, ℚ, numerator, denominator, num, denom, order, ordering, sqrt, abs
-/

namespace Rat

variable (a b c : ℚ)

open Rat

/-- A rational number is called nonnegative if its numerator is nonnegative. -/
protected def Nonneg (r : ℚ) : Prop :=
  0 ≤ r.num
#align rat.nonneg Rat.Nonneg

@[simp]
theorem divInt_nonneg (a : ℤ) {b : ℤ} (h : 0 < b) : (a /. b).Nonneg ↔ 0 ≤ a := by
  generalize ha : a /. b = x; cases' x with n₁ d₁ h₁ c₁; rw [num_den'] at ha
  simp [Rat.Nonneg]
  have d0 := Int.ofNat_lt.2 (Nat.pos_of_ne_zero h₁)
  have := (divInt_eq_iff (ne_of_gt h) (ne_of_gt d0)).1 ha
  constructor <;> intro h₂
  · apply nonneg_of_mul_nonneg_left _ d0
    rw [this]
    exact mul_nonneg h₂ (le_of_lt h)
  · apply nonneg_of_mul_nonneg_left _ h
    rw [← this]
    exact mul_nonneg h₂ (Int.ofNat_zero_le _)
#align rat.mk_nonneg Rat.divInt_nonneg

protected theorem nonneg_add {a b} : Rat.Nonneg a → Rat.Nonneg b → Rat.Nonneg (a + b) :=
  numDenCasesOn' a fun n₁ d₁ h₁ =>
    numDenCasesOn' b fun n₂ d₂ h₂ => by
      have d₁0 : 0 < (d₁ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₁)
      have d₂0 : 0 < (d₂ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₂)
      simp only [d₁0, d₂0, h₁, h₂, mul_pos, divInt_nonneg, add_def'', Ne.def,
        Nat.cast_eq_zero, not_false_iff]
      intro n₁0 n₂0
      apply add_nonneg <;> apply mul_nonneg <;> · first |assumption|apply Int.ofNat_zero_le
#align rat.nonneg_add Rat.nonneg_add

protected theorem nonneg_mul {a b} : Rat.Nonneg a → Rat.Nonneg b → Rat.Nonneg (a * b) :=
  numDenCasesOn' a fun n₁ d₁ h₁ =>
    numDenCasesOn' b fun n₂ d₂ h₂ => by
      have d₁0 : 0 < (d₁ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₁)
      have d₂0 : 0 < (d₂ : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h₂)
      rw [mul_def' d₁0.ne.symm d₂0.ne.symm, divInt_nonneg _ d₁0, divInt_nonneg _ d₂0,
        divInt_nonneg _ (mul_pos d₁0 d₂0)]
      apply mul_nonneg
#align rat.nonneg_mul Rat.nonneg_mul

protected theorem nonneg_antisymm {a} : Rat.Nonneg a → Rat.Nonneg (-a) → a = 0 :=
  numDenCasesOn' a fun n d h => by
    have d0 : 0 < (d : ℤ) := Int.coe_nat_pos.2 (Nat.pos_of_ne_zero h)
    rw [divInt_nonneg _ d0, neg_def, divInt_nonneg _ d0, Right.nonneg_neg_iff,
      divInt_eq_zero d0.ne.symm]
    exact fun h₁ h₂ => le_antisymm h₂ h₁
#align rat.nonneg_antisymm Rat.nonneg_antisymm

protected theorem nonneg_total : Rat.Nonneg a ∨ Rat.Nonneg (-a) := by
  cases' a with n; exact Or.imp_right neg_nonneg_of_nonpos (le_total 0 n)
#align rat.nonneg_total Rat.nonneg_total

instance decidableNonneg : Decidable (Rat.Nonneg a) := by
  cases a; unfold Rat.Nonneg; infer_instance
#align rat.decidable_nonneg Rat.decidableNonneg

-- Porting note: Now `Std` defines `≤` on `Rat`.
-- This is the old mathlib3 definition.
/-- Relation `a ≤ b` on `ℚ` defined as `a ≤ b ↔ Rat.Nonneg (b - a)`. Use `a ≤ b` instead of
`Rat.le a b`. -/
protected def le' (a b : ℚ) := Rat.Nonneg (b - a)
#align rat.le Rat.le'

/-- Define a (dependent) function or prove `∀ r : ℚ, p r` by dealing with rational
numbers of the form `mk' n d` with `d ≠ 0`. -/
-- Porting note: TODO move
@[elab_as_elim]
def numDenCasesOn''.{u} {C : ℚ → Sort u} (a : ℚ)
    (H : ∀ (n : ℤ) (d : ℕ) (nz red), C (mk' n d nz red)) :
    C a :=
  numDenCasesOn a fun n d h h' => by
    rw [←mk_eq_divInt _ _ h.ne' h']
    exact H n d h.ne' _

-- Porting note: TODO can this be shortened?
protected theorem le_iff_Nonneg (a b : ℚ) : a ≤ b ↔ Rat.Nonneg (b - a) :=
  numDenCasesOn'' a fun na da ha hared =>
    numDenCasesOn'' b fun nb db hb hbred => by
      change Rat.blt _ _ = false ↔ _
      unfold Rat.blt
      simp [-divInt_ofNat, mkRat_eq]
      split_ifs with h h'
      · rw [Rat.sub_def]
        simp [Rat.Nonneg]
        simp [normalize_eq]
        apply Int.ediv_neg'
        · rw [sub_neg]
          apply lt_of_lt_of_le
          · apply mul_neg_of_neg_of_pos h.1
            rwa [Nat.cast_pos, pos_iff_ne_zero]
          · apply mul_nonneg h.2 (Nat.cast_nonneg _)
        · simp only [Nat.cast_pos]
          apply Nat.gcd_pos_of_pos_right
          apply mul_pos <;> rwa [pos_iff_ne_zero]
      · simp only [divInt_ofNat, ←zero_iff_num_zero, mkRat_eq_zero hb] at h'
        simp [h', Rat.Nonneg]
      · simp [Rat.Nonneg, Rat.sub_def, normalize_eq]
        refine ⟨fun H => ?_, fun H _ => ?_⟩
        · refine Int.ediv_nonneg ?_ (Nat.cast_nonneg _)
          rw [sub_nonneg]
          push_neg at h
          obtain hb|hb := Ne.lt_or_lt h'
          · apply H
            intro H'
            exact (hb.trans H').false.elim
          · obtain ha|ha := le_or_lt na 0
            · apply le_trans <| mul_nonpos_of_nonpos_of_nonneg ha (Nat.cast_nonneg _)
              exact mul_nonneg hb.le (Nat.cast_nonneg _)
            · exact H (fun _ => ha)
        · rw [←sub_nonneg]
          contrapose! H
          apply Int.ediv_neg' H
          simp only [Nat.cast_pos]
          apply Nat.gcd_pos_of_pos_right
          apply mul_pos <;> rwa [pos_iff_ne_zero]

protected theorem le_def {a b c d : ℤ} (b0 : 0 < b) (d0 : 0 < d) :
    a /. b ≤ c /. d ↔ a * d ≤ c * b := by
  rw [Rat.le_iff_Nonneg]
  show Rat.Nonneg _ ↔ _
  rw [← sub_nonneg]
  simp [sub_eq_add_neg, ne_of_gt b0, ne_of_gt d0, mul_pos d0 b0]
#align rat.le_def Rat.le_def

protected theorem le_refl : a ≤ a := by
  rw [Rat.le_iff_Nonneg]
  show Rat.Nonneg (a - a)
  rw [sub_self]
  exact le_refl (0 : ℤ)
#align rat.le_refl Rat.le_refl

protected theorem le_total : a ≤ b ∨ b ≤ a := by
  have := Rat.nonneg_total (b - a)
  rw [Rat.le_iff_Nonneg, Rat.le_iff_Nonneg]
  rwa [neg_sub] at this
#align rat.le_total Rat.le_total

protected theorem le_antisymm {a b : ℚ} (hab : a ≤ b) (hba : b ≤ a) : a = b := by
  rw [Rat.le_iff_Nonneg] at hab hba
  rw [sub_eq_add_neg] at hba
  rw [←neg_sub, sub_eq_add_neg] at hab
  have := eq_neg_of_add_eq_zero_left (Rat.nonneg_antisymm hba hab)
  rwa [neg_neg] at this
#align rat.le_antisymm Rat.le_antisymm

protected theorem le_trans {a b c : ℚ} (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c := by
  rw [Rat.le_iff_Nonneg] at hab hbc
  have : Rat.Nonneg (b - a + (c - b)) := Rat.nonneg_add hab hbc
  simp_rw [sub_eq_add_neg, add_left_comm (b + -a) c (-b), add_comm (b + -a) (-b),
    add_left_comm (-b) b (-a), add_comm (-b) (-a), add_neg_cancel_comm_assoc,
    ← sub_eq_add_neg] at this
  rw [Rat.le_iff_Nonneg]
  exact this
#align rat.le_trans Rat.le_trans

protected theorem not_le {a b : ℚ} : ¬a ≤ b ↔ b < a := (Bool.not_eq_false _).to_iff

instance linearOrder : LinearOrder ℚ where
  le_refl := Rat.le_refl
  le_trans := @Rat.le_trans
  le_antisymm := @Rat.le_antisymm
  le_total := Rat.le_total
  decidable_le _ _ := by infer_instance
  lt_iff_le_not_le _ _ := by
    rw [← Rat.not_le, and_iff_right_of_imp (Rat.le_total _ _).resolve_left]

-- Extra instances to short-circuit type class resolution
instance : LT ℚ := by infer_instance

instance : DistribLattice ℚ := by infer_instance

instance : Lattice ℚ := by infer_instance

instance : SemilatticeInf ℚ := by infer_instance

instance : SemilatticeSup ℚ := by infer_instance

instance : Inf ℚ := by infer_instance

instance : Sup ℚ := by infer_instance

instance : PartialOrder ℚ := by infer_instance

instance : Preorder ℚ := by infer_instance

protected theorem le_def' {p q : ℚ} : p ≤ q ↔ p.num * q.den ≤ q.num * p.den := by
  rw [← @num_den q, ← @num_den p]
  conv_rhs => simp only [num_den]
  exact Rat.le_def (by exact_mod_cast p.pos) (by exact_mod_cast q.pos)
#align rat.le_def' Rat.le_def'

protected theorem lt_def {p q : ℚ} : p < q ↔ p.num * q.den < q.num * p.den := by
  rw [lt_iff_le_and_ne, Rat.le_def']
  suffices p ≠ q ↔ p.num * q.den ≠ q.num * p.den by
    constructor <;> intro h
    · exact lt_iff_le_and_ne.mpr ⟨h.left, this.mp h.right⟩
    · have tmp := lt_iff_le_and_ne.mp h
      exact ⟨tmp.left, this.mpr tmp.right⟩
  exact not_iff_not.mpr eq_iff_mul_eq_mul
#align rat.lt_def Rat.lt_def

theorem nonneg_iff_zero_le {a} : Rat.Nonneg a ↔ 0 ≤ a := by
  rw [Rat.le_iff_Nonneg]
  show Rat.Nonneg a ↔ Rat.Nonneg (a - 0)
  simp
#align rat.nonneg_iff_zero_le Rat.nonneg_iff_zero_le

theorem num_nonneg_iff_zero_le : ∀ {a : ℚ}, 0 ≤ a.num ↔ 0 ≤ a
  | ⟨n, d, h, c⟩ => @nonneg_iff_zero_le ⟨n, d, h, c⟩
#align rat.num_nonneg_iff_zero_le Rat.num_nonneg_iff_zero_le

protected theorem add_le_add_left {a b c : ℚ} : c + a ≤ c + b ↔ a ≤ b := by
  rw [Rat.le_iff_Nonneg, add_sub_add_left_eq_sub, Rat.le_iff_Nonneg]
#align rat.add_le_add_left Rat.add_le_add_left

protected theorem mul_nonneg {a b : ℚ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b := by
  rw [← nonneg_iff_zero_le] at ha hb⊢; exact Rat.nonneg_mul ha hb
#align rat.mul_nonneg Rat.mul_nonneg

instance : LinearOrderedField ℚ :=
  { Rat.field, Rat.linearOrder, Rat.semiring with
    zero_le_one := by decide
    add_le_add_left := fun a b ab c => Rat.add_le_add_left.2 ab
    mul_pos := fun a b ha hb =>
      lt_of_le_of_ne (Rat.mul_nonneg (le_of_lt ha) (le_of_lt hb))
        (mul_ne_zero (ne_of_lt ha).symm (ne_of_lt hb).symm).symm }

-- Extra instances to short-circuit type class resolution
instance : LinearOrderedCommRing ℚ := by infer_instance

instance : LinearOrderedRing ℚ := by infer_instance

instance : OrderedRing ℚ := by infer_instance

instance : LinearOrderedSemiring ℚ := by infer_instance

instance : OrderedSemiring ℚ := by infer_instance

instance : LinearOrderedAddCommGroup ℚ := by infer_instance

instance : OrderedAddCommGroup ℚ := by infer_instance

instance : OrderedCancelAddCommMonoid ℚ := by infer_instance

instance : OrderedAddCommMonoid ℚ := by infer_instance

theorem num_pos_iff_pos {a : ℚ} : 0 < a.num ↔ 0 < a :=
  lt_iff_lt_of_le_iff_le <| by
    simpa [(by cases a; rfl : (-a).num = -a.num)] using @num_nonneg_iff_zero_le (-a)
#align rat.num_pos_iff_pos Rat.num_pos_iff_pos

theorem div_lt_div_iff_mul_lt_mul {a b c d : ℤ} (b_pos : 0 < b) (d_pos : 0 < d) :
    (a : ℚ) / b < c / d ↔ a * d < c * b := by
  simp only [lt_iff_le_not_le]
  apply and_congr
  · simp [div_num_den, Rat.le_def b_pos d_pos]
  · apply not_congr
    simp [div_num_den, Rat.le_def d_pos b_pos]
#align rat.div_lt_div_iff_mul_lt_mul Rat.div_lt_div_iff_mul_lt_mul

theorem lt_one_iff_num_lt_denom {q : ℚ} : q < 1 ↔ q.num < q.den := by simp [Rat.lt_def]
#align rat.lt_one_iff_num_lt_denom Rat.lt_one_iff_num_lt_denom

theorem abs_def (q : ℚ) : |q| = q.num.natAbs /. q.den := by
  cases' le_total q 0 with hq hq
  · rw [abs_of_nonpos hq]
    rw [← @num_den q, ← divInt_zero_one, Rat.le_def (Int.coe_nat_pos.2 q.pos) zero_lt_one, mul_one,
      zero_mul] at hq
    rw [Int.ofNat_natAbs_of_nonpos hq, ← neg_def, num_den]
  · rw [abs_of_nonneg hq]
    rw [← @num_den q, ← divInt_zero_one, Rat.le_def zero_lt_one (Int.coe_nat_pos.2 q.pos), mul_one,
      zero_mul] at hq
    rw [Int.natAbs_of_nonneg hq, num_den]
#align rat.abs_def Rat.abs_def

end Rat

-- Porting note: `assert_not_exists` not yet implemented.

-- We make some assertions here about declarations that do not need to be in the import dependencies
-- for this file, but have been in the past.
-- assert_not_exists fintype

-- assert_not_exists set.Icc

-- assert_not_exists galois_connection

-- These are less significant, but should not be relaxed until at least after port to Lean 4.
-- assert_not_exists LinearOrderedCommGroupWithZero


/-
Copyright (c) 2022 Alexander Bentkamp. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alexander Bentkamp
-/
import analysis.inner_product_space.spectrum

/-! # Hermitian matrices

This file defines hermitian matrices and some basic results about them.

## Main definition

 * `matrix.is_hermitian` : a matrix `A : matrix n n α` is hermitian if `Aᴴ = A`.

## Tags

self-adjoint matrix, hermitian matrix

-/

namespace matrix

variables {α β : Type*} {m n : Type*} {A : matrix n n α}

open_locale matrix

local notation `⟪`x`, `y`⟫` := @inner α (pi_Lp 2 (λ (_ : n), α)) _ x y

section non_unital_semiring

variables [non_unital_semiring α] [star_ring α] [non_unital_semiring β] [star_ring β]

/-- A matrix is hermitian if it is equal to its conjugate transpose. On the reals, this definition
captures symmetric matrices. -/
def is_hermitian (A : matrix n n α) : Prop := Aᴴ = A

lemma is_hermitian.eq {A : matrix n n α} (h : A.is_hermitian) : Aᴴ = A := h

@[ext]
lemma is_hermitian.ext {A : matrix n n α} : (∀ i j, star (A j i) = A i j) → A.is_hermitian :=
by { intros h, ext i j, exact h i j }

lemma is_hermitian.apply {A : matrix n n α} (h : A.is_hermitian) (i j : n) : star (A j i) = A i j :=
by { unfold is_hermitian at h, rw [← h, conj_transpose_apply, star_star, h] }

lemma is_hermitian.ext_iff {A : matrix n n α} : A.is_hermitian ↔ ∀ i j, star (A j i) = A i j :=
⟨is_hermitian.apply, is_hermitian.ext⟩

lemma is_hermitian_mul_conj_transpose_self [fintype n] (A : matrix n n α) :
  (A ⬝ Aᴴ).is_hermitian :=
by rw [is_hermitian, conj_transpose_mul, conj_transpose_conj_transpose]

lemma is_hermitian_transpose_mul_self [fintype n] (A : matrix n n α) :
  (Aᴴ ⬝ A).is_hermitian :=
by rw [is_hermitian, conj_transpose_mul, conj_transpose_conj_transpose]

lemma is_hermitian_add_transpose_self (A : matrix n n α) :
  (A + Aᴴ).is_hermitian :=
by simp [is_hermitian, add_comm]

lemma is_hermitian_transpose_add_self (A : matrix n n α) :
  (Aᴴ + A).is_hermitian :=
by simp [is_hermitian, add_comm]

@[simp] lemma is_hermitian_zero :
  (0 : matrix n n α).is_hermitian :=
conj_transpose_zero

-- TODO: move
lemma conj_transpose_map {A : matrix n n α} (f : α → β) (hf : f ∘ star = star ∘ f) :
  Aᴴ.map f = (A.map f)ᴴ :=
by rw [conj_transpose, conj_transpose, ←transpose_map, map_map, map_map, hf]

@[simp] lemma is_hermitian.map {A : matrix n n α} (h : A.is_hermitian) (f : α → β)
    (hf : f ∘ star = star ∘ f) :
  (A.map f).is_hermitian :=
by {refine (conj_transpose_map f hf).symm.trans _, rw h.eq }

@[simp] lemma is_hermitian.transpose {A : matrix n n α} (h : A.is_hermitian) :
  Aᵀ.is_hermitian :=
by { rw [is_hermitian, conj_transpose, transpose_map], congr, exact h }

@[simp] lemma is_hermitian.conj_transpose {A : matrix n n α} (h : A.is_hermitian) :
  Aᴴ.is_hermitian :=
h.transpose.map _ rfl

@[simp] lemma is_hermitian.add {A B : matrix n n α} (hA : A.is_hermitian) (hB : B.is_hermitian) :
  (A + B).is_hermitian :=
(conj_transpose_add _ _).trans (hA.symm ▸ hB.symm ▸ rfl)

@[simp] lemma is_hermitian.minor {A : matrix n n α} (h : A.is_hermitian) (f : m → n) :
  (A.minor f f).is_hermitian :=
(conj_transpose_minor _ _ _).trans (h.symm ▸ rfl)

/-- The real diagonal matrix `diagonal v` is hermitian. -/
@[simp] lemma is_hermitian_diagonal [decidable_eq n] (v : n → ℝ) :
  (diagonal v).is_hermitian :=
diagonal_conj_transpose _

/-- A block matrix `A.from_blocks B C D` is hermitian,
    if `A` and `D` are hermitian and `Bᴴ = C`. -/
lemma is_hermitian.from_blocks
  {A : matrix m m α} {B : matrix m n α} {C : matrix n m α} {D : matrix n n α}
  (hA : A.is_hermitian) (hBC : Bᴴ = C) (hD : D.is_hermitian) :
  (A.from_blocks B C D).is_hermitian :=
begin
  have hCB : Cᴴ = B, {rw ← hBC, simp},
  unfold matrix.is_hermitian,
  rw from_blocks_conj_transpose,
  congr;
  assumption
end

/-- This is the `iff` version of `matrix.is_hermitian.from_blocks`. -/
lemma is_hermitian_from_blocks_iff
  {A : matrix m m α} {B : matrix m n α} {C : matrix n m α} {D : matrix n n α} :
  (A.from_blocks B C D).is_hermitian ↔ A.is_hermitian ∧ Bᴴ = C ∧ Cᴴ = B ∧ D.is_hermitian :=
⟨λ h, ⟨congr_arg to_blocks₁₁ h, congr_arg to_blocks₂₁ h,
       congr_arg to_blocks₁₂ h, congr_arg to_blocks₂₂ h⟩,
 λ ⟨hA, hBC, hCB, hD⟩, is_hermitian.from_blocks hA hBC hD⟩

end non_unital_semiring

section semiring

variables [semiring α] [star_ring α] [semiring β] [star_ring β]

@[simp] lemma is_hermitian_one [decidable_eq n] :
  (1 : matrix n n α).is_hermitian :=
conj_transpose_one

end semiring

section ring

variables [ring α] [star_ring α] [ring β] [star_ring β]

@[simp] lemma is_hermitian.neg {A : matrix n n α} (h : A.is_hermitian) :
  (-A).is_hermitian :=
(conj_transpose_neg _).trans (congr_arg _ h)

@[simp] lemma is_hermitian.sub {A B : matrix n n α} (hA : A.is_hermitian) (hB : B.is_hermitian) :
  (A - B).is_hermitian :=
(conj_transpose_sub _ _).trans (hA.symm ▸ hB.symm ▸ rfl)

end ring

section is_R_or_C

variables [is_R_or_C α] [is_R_or_C β]

/-- A matrix is hermitian iff the corresponding linear map is self adjoint. -/
lemma is_hermitian_iff_is_self_adjoint [fintype n] [decidable_eq n] {A : matrix n n α} :
  is_hermitian A ↔ inner_product_space.is_self_adjoint
    ((pi_Lp.linear_equiv α (λ _ : n, α)).symm.conj A.to_lin' : module.End α (pi_Lp 2 _)) :=
begin
  rw [inner_product_space.is_self_adjoint, (pi_Lp.equiv 2 (λ _ : n, α)).symm.surjective.forall₂],
  simp only [linear_equiv.conj_apply, linear_map.comp_apply, linear_equiv.coe_coe,
    pi_Lp.linear_equiv_apply, pi_Lp.linear_equiv_symm_apply, linear_equiv.symm_symm],
  simp_rw [euclidean_space.inner_eq_star_dot_product, equiv.apply_symm_apply, to_lin'_apply,
    star_mul_vec, dot_product_mul_vec],
  split,
  { rintro (h : Aᴴ = A) x y,
    rw h },
  { intro h,
    ext i j,
    simpa only [(pi.single_star i 1).symm, ← star_mul_vec, mul_one, dot_product_single,
      single_vec_mul, star_one, one_mul] using
        h (@pi.single _ _ _ (λ i, add_zero_class.to_has_zero α) i 1)
          (@pi.single _ _ _ (λ i, add_zero_class.to_has_zero α) j 1) }
end

end is_R_or_C

end matrix


variables (w x y z : ℕ) (p : ℕ → Prop)

local attribute [simp] mul_comm mul_assoc mul_left_comm

example (h : p (x * y + z * w  * x)) : p (x * w * z + y * x) :=
  by { simp [add_comm] at *, assumption }
-- lean 3.6 removes simp attributes from add_comm
-- i.e., now it is required to provide that lemma to solve this example.

example (h₁ : p (1 * x + y)) (h₂ : p  (x * z * 1)) :
  p (y + 0 + x) ∧ p (z * x) :=
  by { simp [add_comm] at *, split; assumption }
-- lean 3.6 removes simp attributes from add_comm
-- i.e., now it is required to provide that lemma to solve this example.


/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.multiset.basic
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Sections of a multiset
-/

namespace multiset

/--
The sections of a multiset of multisets `s` consists of all those multisets
which can be put in bijection with `s`, so each element is an member of the corresponding multiset.
-/
def sections {α : Type u_1} (s : multiset (multiset α)) : multiset (multiset α) :=
  multiset.rec_on s (singleton 0)
    (fun (s : multiset α) (_x c : multiset (multiset α)) => bind s fun (a : α) => map (cons a) c) sorry

@[simp] theorem sections_zero {α : Type u_1} : sections 0 = 0 ::ₘ 0 :=
  rfl

@[simp] theorem sections_cons {α : Type u_1} (s : multiset (multiset α)) (m : multiset α) : sections (m ::ₘ s) = bind m fun (a : α) => map (cons a) (sections s) :=
  rec_on_cons m s

theorem coe_sections {α : Type u_1} (l : List (List α)) : sections ↑(list.map (fun (l : List α) => ↑l) l) = ↑(list.map (fun (l : List α) => ↑l) (list.sections l)) := sorry

@[simp] theorem sections_add {α : Type u_1} (s : multiset (multiset α)) (t : multiset (multiset α)) : sections (s + t) = bind (sections s) fun (m : multiset α) => map (Add.add m) (sections t) := sorry

theorem mem_sections {α : Type u_1} {s : multiset (multiset α)} {a : multiset α} : a ∈ sections s ↔ rel (fun (s : multiset α) (a : α) => a ∈ s) s a := sorry

theorem card_sections {α : Type u_1} {s : multiset (multiset α)} : coe_fn card (sections s) = prod (map (⇑card) s) := sorry

theorem prod_map_sum {α : Type u_1} [comm_semiring α] {s : multiset (multiset α)} : prod (map sum s) = sum (map prod (sections s)) := sorry


import ..basic .algebra2

noncomputable theory
universes u v w
variables {α : Type*} {β : Type*} [comm_ring α] [comm_ring β]

open set

/-- An element in a ring is nilpotent if a integer power of it is equal to `0` -/
def is_nilpotent {α : Type u} [comm_semiring α] (x : α) : Prop := ∃ n : ℕ, x^n = 0
/-- An ring is reduced if `0` is its only nilpotent element -/
def is_reduced (α : Type u) [comm_semiring α] : Prop := ∀(x : α), is_nilpotent x → x = 0
/-- A ring is algebraically closed if every non-constant polynomial has a root in α -/
def is_algebraically_closed (α : Type u) [comm_semiring α] [decidable_eq α] : Prop :=
∀(p : polynomial α), p.degree > 0 → ∃x, p.is_root x

namespace ideal

/-- An ideal is radical if it is equal to its radical -/
def is_radical {α : Type u} [comm_ring α] (I : ideal α) : Prop := I = I.radical
/-- The type of maximal ideals in a ring -/
@[reducible] def maximal_ideal (α : Type u) [comm_ring α] : Type u :=
subtype (ideal.is_maximal : ideal α → Prop)
/-- The type of prime ideals in a ring -/
@[reducible] def prime_ideal (α : Type u) [comm_ring α] : Type u :=
subtype (ideal.is_prime : ideal α → Prop)
/-- The type of radical ideals in a ring -/
@[reducible] def radical_ideal (α : Type u) [comm_ring α] : Type u :=
subtype (ideal.is_radical : ideal α → Prop)

/-- An ideal where the quotient forms a field is a maximal ideal -/
lemma is_maximal_of_field {I : ideal α}
  (h1 : has_inv I.quotient)
  (h2 : zero_ne_one_class I.quotient)
  (h3 : ∀{a : I.quotient}, a ≠ 0 → a * a⁻¹ = 1)
  (h4 : ∀{a : I.quotient}, a ≠ 0 → a⁻¹ * a = 1) : is_maximal I :=
omitted

/-- The kernel of a ring homomorphism -/
protected def ker (f : α → β) [is_ring_hom f] : ideal α :=
linear_map.ker (alg_hom.of_ring_hom f).to_linear_map

/-- `x` is an element of the kernel of `f` iff `f x = 0` -/
@[simp] lemma mem_ker (f : α → β) [is_ring_hom f] {x : α} : x ∈ ideal.ker f ↔ f x = 0 :=
linear_map.mem_ker

/-- The quotient of a ring by a radical ideal is reduced. -/
lemma is_reduced_quotient {I : ideal α} (h : I.is_radical) : is_reduced (quotient I) :=
omitted

/-- Every integral domain is reduced -/
lemma is_reduced_integral_domain {α : Type*} [integral_domain α] : is_reduced α :=
omitted

section
variables {R : Type u} [comm_ring R]
variables {A : Type*} {B : Type*} [comm_ring A] [comm_ring B] [algebra R A] [algebra R B]

/-- The tensor product of two reduced algebras is reduced -/
lemma is_reduced_tensor (hA : is_reduced A) (hB : is_reduced B) : is_reduced (A ⊗[R] B) :=
omitted
end

/-- the map f → range f is a ring homomorphism -/
instance range_factorization.is_ring_hom (f : α → β) [is_ring_hom f] :
  is_ring_hom (range_factorization f) :=
begin
  constructor; intros; apply subtype.eq; simp [range_factorization],
  rw [is_ring_hom.map_one f], refl,
  rw [is_ring_hom.map_mul f], refl,
  rw [is_ring_hom.map_add f], refl
end

/-- the map out of the quotient of the kernel to the range of a map -/
def quotient_ker_map (f : α → β) [is_ring_hom f] : (ideal.ker f).quotient → range f :=
by { haveI : is_ring_hom (range_factorization f) := by apply_instance,
     apply quotient.lift _ (range_factorization f), intros a h,
     apply subtype.eq, rw [ideal.mem_ker] at h, exact h }

/-- The map above is a ring homomorphism -/
instance quotient_ker_map.is_ring_hom (f : α → β) [is_ring_hom f] :
  is_ring_hom (quotient_ker_map f) :=
by { haveI : is_ring_hom (range_factorization f) := by apply_instance,
     dsimp [quotient_ker_map], apply_instance }

/- the inverse map to the above map -/
def quotient_ker_inv (f : α → β) [is_ring_hom f] : range f → (ideal.ker f).quotient :=
λ x, ideal.quotient.mk (ideal.ker f) (classical.some x.property)

/-- The maps above form an equivalence -/
def quotient_ker_equiv (f : α → β) [is_ring_hom f] : (ideal.ker f).quotient ≃ range f :=
⟨quotient_ker_map f, quotient_ker_inv f, omitted, omitted⟩

/- To do: Every field is a subfield of an algebraically closed field -/

end ideal


/-
Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel, Scott Morrison
-/
import category_theory.limits.shapes.kernels

/-!
# The abelian image and coimage.

In an abelian category we usually want the image of a morphism `f` to be defined as
`kernel (cokernel.π f)`, and the coimage to be defined as `cokernel (kernel.ι f)`.

We make these definitions here, as `abelian.image f` and `abelian.coimage f`
(without assuming the category is actually abelian),
and later relate these to the usual categorical notions when in an abelian category.

There is a canonical morphism `coimage_image_comparison : abelian.coimage f ⟶ abelian.image f`.
Later we show that this is always an isomorphism in an abelian category,
and conversely a category with (co)kernels and finite products in which this morphism
is always an isomorphism is an abelian category.
-/

noncomputable theory

universes v u

open category_theory
open category_theory.limits

namespace category_theory.abelian

variables {C : Type u} [category.{v} C] [has_zero_morphisms C] [has_kernels C] [has_cokernels C]
variables {P Q : C} (f : P ⟶ Q)

section image

/-- The kernel of the cokernel of `f` is called the (abelian) image of `f`. -/
protected abbreviation image : C := kernel (cokernel.π f)

/-- The inclusion of the image into the codomain. -/
protected abbreviation image.ι : abelian.image f ⟶ Q :=
kernel.ι (cokernel.π f)

/-- There is a canonical epimorphism `p : P ⟶ image f` for every `f`. -/
protected abbreviation factor_thru_image : P ⟶ abelian.image f :=
kernel.lift (cokernel.π f) f $ cokernel.condition f

/-- `f` factors through its image via the canonical morphism `p`. -/
@[simp, reassoc] protected lemma image.fac :
  abelian.factor_thru_image f ≫ image.ι f = f :=
kernel.lift_ι _ _ _

instance mono_factor_thru_image [mono f] : mono (abelian.factor_thru_image f) :=
mono_of_mono_fac $ image.fac f

end image

section coimage

/-- The cokernel of the kernel of `f` is called the (abelian) coimage of `f`. -/
protected abbreviation coimage : C := cokernel (kernel.ι f)

/-- The projection onto the coimage. -/
protected abbreviation coimage.π : P ⟶ abelian.coimage f :=
cokernel.π (kernel.ι f)

/-- There is a canonical monomorphism `i : coimage f ⟶ Q`. -/
protected abbreviation factor_thru_coimage : abelian.coimage f ⟶ Q :=
cokernel.desc (kernel.ι f) f $ kernel.condition f

/-- `f` factors through its coimage via the canonical morphism `p`. -/
protected lemma coimage.fac : coimage.π f ≫ abelian.factor_thru_coimage f = f :=
cokernel.π_desc _ _ _

instance epi_factor_thru_coimage [epi f] : epi (abelian.factor_thru_coimage f) :=
epi_of_epi_fac $ coimage.fac f

end coimage

/--
The canonical map from the abelian coimage to the abelian image.
In any abelian category this is an isomorphism.

Conversely, any additive category with kernels and cokernels and
in which this is always an isomorphism, is abelian.

See https://stacks.math.columbia.edu/tag/0107
-/
def coimage_image_comparison : abelian.coimage f ⟶ abelian.image f :=
cokernel.desc (kernel.ι f) (kernel.lift (cokernel.π f) f (by simp)) $ (by { ext, simp, })

/--
An alternative formulation of the canonical map from the abelian coimage to the abelian image.
-/
def coimage_image_comparison' : abelian.coimage f ⟶ abelian.image f :=
kernel.lift (cokernel.π f) (cokernel.desc (kernel.ι f) f (by simp)) (by { ext, simp, })

lemma coimage_image_comparison_eq_coimage_image_comparison' :
  coimage_image_comparison f = coimage_image_comparison' f :=
by { ext, simp [coimage_image_comparison, coimage_image_comparison'], }

@[simp, reassoc]
lemma coimage_image_factorisation :
  coimage.π f ≫ coimage_image_comparison f ≫ image.ι f = f :=
by simp [coimage_image_comparison]

end category_theory.abelian


import data.real.sqrt
import data.complex.exponential

open filter real
open_locale topological_space

theorem part_i :
  tendsto (λ n : ℕ, n / (n + 5) : ℕ → ℝ) at_top (𝓝 1) :=
begin
  sorry
end

theorem part_ii :
  tendsto (λ n : ℕ, 1 / sqrt (n + 5)) at_top (𝓝 0) :=
begin
  sorry
end

theorem part_iii :
  tendsto (λ n : ℕ, ↑n * sqrt n / (n + 5)) at_top at_top :=
begin
  sorry
end

theorem part_iv :
  tendsto (λ n : ℕ, ((-1)^n * sin n) / sqrt n ) at_top (𝓝 0) :=
begin
  sorry
end

theorem part_v :
  tendsto (λ n : ℕ, (↑n^3 - 2*sqrt n + 7) / (2 - n^2 - 5*n^3)) at_top (𝓝 (-1/5)) :=
begin
  sorry
end

theorem part_vi (n : ℕ) :
  ¬∃ l : ℝ, tendsto (λ n, (1 - (-1)^n * n) / n : ℕ → ℝ) at_top (𝓝 l) :=
begin
  sorry
end

theorem part_vii :
  tendsto (λ n : ℕ, sqrt (n + 1) - sqrt n) at_top (𝓝 0) :=
begin
  sorry
end


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

! This file was ported from Lean 3 source module linear_algebra.general_linear_group
! leanprover-community/mathlib commit 2705404e701abc6b3127da906f40bae062a169c9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Module.Equiv

/-!
# The general linear group of linear maps

The general linear group is defined to be the group of invertible linear maps from `M` to itself.

See also `Matrix.GeneralLinearGroup`

## Main definitions

* `LinearMap.GeneralLinearGroup`

-/

variable (R M : Type _)

namespace LinearMap

variable [Semiring R] [AddCommMonoid M] [Module R M]

/-- The group of invertible linear maps from `M` to itself -/
@[reducible]
def GeneralLinearGroup :=
  (M →ₗ[R] M)ˣ
#align linear_map.general_linear_group LinearMap.GeneralLinearGroup

namespace GeneralLinearGroup

variable {R M}

-- Porting note: This is not necessary anymore
-- instance : CoeFun (GeneralLinearGroup R M) fun _ ↦ M → M := by infer_instance

/-- An invertible linear map `f` determines an equivalence from `M` to itself. -/
def toLinearEquiv (f : GeneralLinearGroup R M) : M ≃ₗ[R] M :=
  { f.val with
    invFun := f.inv.toFun
    left_inv := fun m ↦ show (f.inv * f.val) m = m by erw [f.inv_val]; simp
    right_inv := fun m ↦ show (f.val * f.inv) m = m by erw [f.val_inv]; simp }
#align linear_map.general_linear_group.to_linear_equiv LinearMap.GeneralLinearGroup.toLinearEquiv

/-- An equivalence from `M` to itself determines an invertible linear map. -/
def ofLinearEquiv (f : M ≃ₗ[R] M) : GeneralLinearGroup R M where
  val := f
  inv := (f.symm : M →ₗ[R] M)
  val_inv := LinearMap.ext fun _ ↦ f.apply_symm_apply _
  inv_val := LinearMap.ext fun _ ↦ f.symm_apply_apply _
#align linear_map.general_linear_group.of_linear_equiv LinearMap.GeneralLinearGroup.ofLinearEquiv

variable (R M)

/-- The general linear group on `R` and `M` is multiplicatively equivalent to the type of linear
equivalences between `M` and itself. -/
def generalLinearEquiv : GeneralLinearGroup R M ≃* M ≃ₗ[R] M where
  toFun := toLinearEquiv
  invFun := ofLinearEquiv
  left_inv f := by ext; rfl
  right_inv f := by ext; rfl
  map_mul' x y := by ext; rfl
#align linear_map.general_linear_group.general_linear_equiv
  LinearMap.GeneralLinearGroup.generalLinearEquiv

@[simp]
theorem generalLinearEquiv_to_linearMap (f : GeneralLinearGroup R M) :
    (generalLinearEquiv R M f : M →ₗ[R] M) = f := by ext; rfl
#align linear_map.general_linear_group.general_linear_equiv_to_linear_map LinearMap.GeneralLinearGroup.generalLinearEquiv_to_linearMap

@[simp]
theorem coeFn_generalLinearEquiv (f : GeneralLinearGroup R M) :
    (generalLinearEquiv R M f) = (f : M → M) := rfl
#align linear_map.general_linear_group.coe_fn_general_linear_equiv LinearMap.GeneralLinearGroup.coeFn_generalLinearEquiv

end GeneralLinearGroup

end LinearMap