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

! This file was ported from Lean 3 source module order.hom.order
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Logic.Function.Iterate
import Mathbin.Order.GaloisConnection
import Mathbin.Order.Hom.Basic

/-!
# Lattice structure on order homomorphisms

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

This file defines the lattice structure on order homomorphisms, which are bundled
monotone functions.

## Main definitions

 * `order_hom.complete_lattice`: if `β` is a complete lattice, so is `α →o β`

## Tags

monotone map, bundled morphism
-/

namespace OrderHom

variable {α β : Type _}

section Preorder

variable [Preorder α]

@[simps]
instance [SemilatticeSup β] : Sup (α →o β) where sup f g := ⟨fun a => f a ⊔ g a, f.mono.sup g.mono⟩

instance [SemilatticeSup β] : SemilatticeSup (α →o β) :=
  { (_ : PartialOrder (α →o β)) with
    sup := Sup.sup
    le_sup_left := fun a b x => le_sup_left
    le_sup_right := fun a b x => le_sup_right
    sup_le := fun a b c h₀ h₁ x => sup_le (h₀ x) (h₁ x) }

@[simps]
instance [SemilatticeInf β] : Inf (α →o β) where inf f g := ⟨fun a => f a ⊓ g a, f.mono.inf g.mono⟩

instance [SemilatticeInf β] : SemilatticeInf (α →o β) :=
  { (_ : PartialOrder (α →o β)), (dualIso α β).symm.toGaloisInsertion.liftSemilatticeInf with
    inf := (· ⊓ ·) }

instance [Lattice β] : Lattice (α →o β) :=
  { (_ : SemilatticeSup (α →o β)), (_ : SemilatticeInf (α →o β)) with }

@[simps]
instance [Preorder β] [OrderBot β] : Bot (α →o β) where bot := const α ⊥

instance [Preorder β] [OrderBot β] : OrderBot (α →o β)
    where
  bot := ⊥
  bot_le a x := bot_le

@[simps]
instance [Preorder β] [OrderTop β] : Top (α →o β) where top := const α ⊤

instance [Preorder β] [OrderTop β] : OrderTop (α →o β)
    where
  top := ⊤
  le_top a x := le_top

instance [CompleteLattice β] : InfSet (α →o β)
    where infₛ s := ⟨fun x => ⨅ f ∈ s, (f : _) x, fun x y h => infᵢ₂_mono fun f _ => f.mono h⟩

/- warning: order_hom.Inf_apply -> OrderHom.infₛ_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] [_inst_2 : CompleteLattice.{u2} β] (s : Set.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (InfSet.infₛ.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasInf.{u1, u2} α β _inst_1 _inst_2) s) x) (infᵢ.{u2, succ (max u1 u2)} β (CompleteSemilatticeInf.toHasInf.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)) (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (f : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => infᵢ.{u2, 0} β (CompleteSemilatticeInf.toHasInf.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)) (Membership.Mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.hasMem.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) (fun (H : Membership.Mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.hasMem.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) => coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) f x)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] [_inst_2 : CompleteLattice.{u2} β] (s : Set.{max u2 u1} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (InfSet.infₛ.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.instInfSetOrderHomToPreorderToPartialOrderToCompleteSemilatticeInf.{u1, u2} α β _inst_1 _inst_2) s) x) (infᵢ.{u2, succ (max u1 u2)} β (CompleteLattice.toInfSet.{u2} β _inst_2) (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (f : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => infᵢ.{u2, 0} β (CompleteLattice.toInfSet.{u2} β _inst_2) (Membership.mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u2 u1} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.instMembershipSet.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) (fun (H : Membership.mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u2 u1} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.instMembershipSet.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) => OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) f x)))
Case conversion may be inaccurate. Consider using '#align order_hom.Inf_apply OrderHom.infₛ_applyₓ'. -/
@[simp]
theorem infₛ_apply [CompleteLattice β] (s : Set (α →o β)) (x : α) : infₛ s x = ⨅ f ∈ s, (f : _) x :=
  rfl
#align order_hom.Inf_apply OrderHom.infₛ_apply

/- warning: order_hom.infi_apply -> OrderHom.infᵢ_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (infᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i)) x) (infᵢ.{u2, u3} β (CompleteSemilatticeInf.toHasInf.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)) ι (fun (i : ι) => coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (f i) x))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (infᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.instInfSetOrderHomToPreorderToPartialOrderToCompleteSemilatticeInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i)) x) (infᵢ.{u2, u3} β (CompleteLattice.toInfSet.{u2} β _inst_2) ι (fun (i : ι) => OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (f i) x))
Case conversion may be inaccurate. Consider using '#align order_hom.infi_apply OrderHom.infᵢ_applyₓ'. -/
theorem infᵢ_apply {ι : Sort _} [CompleteLattice β] (f : ι → α →o β) (x : α) :
    (⨅ i, f i) x = ⨅ i, f i x :=
  (infₛ_apply _ _).trans infᵢ_range
#align order_hom.infi_apply OrderHom.infᵢ_apply

/- warning: order_hom.coe_infi -> OrderHom.coe_infᵢ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))), Eq.{max (succ u1) (succ u2)} ((fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (infᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (infᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (infᵢ.{max u1 u2, u3} ((fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (infᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (Pi.infSet.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => CompleteSemilatticeInf.toHasInf.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) ι (fun (i : ι) => coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (f i)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))), Eq.{max (succ u1) (succ u2)} (α -> β) (OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (infᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.instInfSetOrderHomToPreorderToPartialOrderToCompleteSemilatticeInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (infᵢ.{max u1 u2, u3} (α -> β) (Pi.infSet.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => CompleteLattice.toInfSet.{u2} β _inst_2)) ι (fun (i : ι) => OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (f i)))
Case conversion may be inaccurate. Consider using '#align order_hom.coe_infi OrderHom.coe_infᵢₓ'. -/
@[simp, norm_cast]
theorem coe_infᵢ {ι : Sort _} [CompleteLattice β] (f : ι → α →o β) :
    ((⨅ i, f i : α →o β) : α → β) = ⨅ i, f i :=
  funext fun x => (infᵢ_apply f x).trans (@infᵢ_apply _ _ _ _ (fun i => f i) _).symm
#align order_hom.coe_infi OrderHom.coe_infᵢ

instance [CompleteLattice β] : SupSet (α →o β)
    where supₛ s := ⟨fun x => ⨆ f ∈ s, (f : _) x, fun x y h => supᵢ₂_mono fun f _ => f.mono h⟩

/- warning: order_hom.Sup_apply -> OrderHom.supₛ_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] [_inst_2 : CompleteLattice.{u2} β] (s : Set.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (SupSet.supₛ.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasSup.{u1, u2} α β _inst_1 _inst_2) s) x) (supᵢ.{u2, succ (max u1 u2)} β (CompleteSemilatticeSup.toHasSup.{u2} β (CompleteLattice.toCompleteSemilatticeSup.{u2} β _inst_2)) (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (f : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => supᵢ.{u2, 0} β (CompleteSemilatticeSup.toHasSup.{u2} β (CompleteLattice.toCompleteSemilatticeSup.{u2} β _inst_2)) (Membership.Mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.hasMem.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) (fun (H : Membership.Mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.hasMem.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) => coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) f x)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] [_inst_2 : CompleteLattice.{u2} β] (s : Set.{max u2 u1} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (SupSet.supₛ.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.instSupSetOrderHomToPreorderToPartialOrderToCompleteSemilatticeInf.{u1, u2} α β _inst_1 _inst_2) s) x) (supᵢ.{u2, succ (max u1 u2)} β (CompleteLattice.toSupSet.{u2} β _inst_2) (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (f : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => supᵢ.{u2, 0} β (CompleteLattice.toSupSet.{u2} β _inst_2) (Membership.mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u2 u1} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.instMembershipSet.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) (fun (H : Membership.mem.{max u1 u2, max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (Set.{max u2 u1} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (Set.instMembershipSet.{max u1 u2} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) f s) => OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) f x)))
Case conversion may be inaccurate. Consider using '#align order_hom.Sup_apply OrderHom.supₛ_applyₓ'. -/
@[simp]
theorem supₛ_apply [CompleteLattice β] (s : Set (α →o β)) (x : α) : supₛ s x = ⨆ f ∈ s, (f : _) x :=
  rfl
#align order_hom.Sup_apply OrderHom.supₛ_apply

/- warning: order_hom.supr_apply -> OrderHom.supᵢ_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (supᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasSup.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i)) x) (supᵢ.{u2, u3} β (CompleteSemilatticeSup.toHasSup.{u2} β (CompleteLattice.toCompleteSemilatticeSup.{u2} β _inst_2)) ι (fun (i : ι) => coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (f i) x))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))) (x : α), Eq.{succ u2} β (OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (supᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.instSupSetOrderHomToPreorderToPartialOrderToCompleteSemilatticeInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i)) x) (supᵢ.{u2, u3} β (CompleteLattice.toSupSet.{u2} β _inst_2) ι (fun (i : ι) => OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (f i) x))
Case conversion may be inaccurate. Consider using '#align order_hom.supr_apply OrderHom.supᵢ_applyₓ'. -/
theorem supᵢ_apply {ι : Sort _} [CompleteLattice β] (f : ι → α →o β) (x : α) :
    (⨆ i, f i) x = ⨆ i, f i x :=
  (supₛ_apply _ _).trans supᵢ_range
#align order_hom.supr_apply OrderHom.supᵢ_apply

/- warning: order_hom.coe_supr -> OrderHom.coe_supᵢ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))), Eq.{max (succ u1) (succ u2)} ((fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (supᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasSup.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (supᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasSup.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (supᵢ.{max u1 u2, u3} ((fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (supᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.hasSup.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (Pi.supSet.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => CompleteSemilatticeSup.toHasSup.{u2} β (CompleteLattice.toCompleteSemilatticeSup.{u2} β _inst_2))) ι (fun (i : ι) => coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (fun (_x : OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) => α -> β) (OrderHom.hasCoeToFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (f i)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u1} α] {ι : Sort.{u3}} [_inst_2 : CompleteLattice.{u2} β] (f : ι -> (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))))), Eq.{max (succ u1) (succ u2)} (α -> β) (OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (supᵢ.{max u1 u2, u3} (OrderHom.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2)))) (OrderHom.instSupSetOrderHomToPreorderToPartialOrderToCompleteSemilatticeInf.{u1, u2} α β _inst_1 _inst_2) ι (fun (i : ι) => f i))) (supᵢ.{max u1 u2, u3} (α -> β) (Pi.supSet.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => CompleteLattice.toSupSet.{u2} β _inst_2)) ι (fun (i : ι) => OrderHom.toFun.{u1, u2} α β _inst_1 (PartialOrder.toPreorder.{u2} β (CompleteSemilatticeInf.toPartialOrder.{u2} β (CompleteLattice.toCompleteSemilatticeInf.{u2} β _inst_2))) (f i)))
Case conversion may be inaccurate. Consider using '#align order_hom.coe_supr OrderHom.coe_supᵢₓ'. -/
@[simp, norm_cast]
theorem coe_supᵢ {ι : Sort _} [CompleteLattice β] (f : ι → α →o β) :
    ((⨆ i, f i : α →o β) : α → β) = ⨆ i, f i :=
  funext fun x => (supᵢ_apply f x).trans (@supᵢ_apply _ _ _ _ (fun i => f i) _).symm
#align order_hom.coe_supr OrderHom.coe_supᵢ

instance [CompleteLattice β] : CompleteLattice (α →o β) :=
  { (_ : Lattice (α →o β)), OrderHom.orderTop,
    OrderHom.orderBot with
    supₛ := supₛ
    le_sup := fun s f hf x => le_supᵢ_of_le f (le_supᵢ _ hf)
    sup_le := fun s f hf x => supᵢ₂_le fun g hg => hf g hg x
    infₛ := infₛ
    le_inf := fun s f hf x => le_infᵢ₂ fun g hg => hf g hg x
    inf_le := fun s f hf x => infᵢ_le_of_le f (infᵢ_le _ hf) }

/- warning: order_hom.iterate_sup_le_sup_iff -> OrderHom.iterate_sup_le_sup_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : SemilatticeSup.{u1} α] (f : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))), Iff (forall (n₁ : Nat) (n₂ : Nat) (a₁ : α) (a₂ : α), LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (Nat.iterate.{succ u1} α (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (fun (_x : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) => α -> α) (OrderHom.hasCoeToFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) f) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n₁ n₂) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_2) a₁ a₂)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_2) (Nat.iterate.{succ u1} α (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (fun (_x : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) => α -> α) (OrderHom.hasCoeToFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) f) n₁ a₁) (Nat.iterate.{succ u1} α (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (fun (_x : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) => α -> α) (OrderHom.hasCoeToFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) f) n₂ a₂))) (forall (a₁ : α) (a₂ : α), LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (fun (_x : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) => α -> α) (OrderHom.hasCoeToFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) f (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_2) a₁ a₂)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_2) (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (fun (_x : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) => α -> α) (OrderHom.hasCoeToFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) f a₁) a₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : SemilatticeSup.{u1} α] (f : OrderHom.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))), Iff (forall (n₁ : Nat) (n₂ : Nat) (a₁ : α) (a₂ : α), LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (Nat.iterate.{succ u1} α (OrderHom.toFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) f) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n₁ n₂) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_2) a₁ a₂)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_2) (Nat.iterate.{succ u1} α (OrderHom.toFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) f) n₁ a₁) (Nat.iterate.{succ u1} α (OrderHom.toFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) f) n₂ a₂))) (forall (a₁ : α) (a₂ : α), LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2))) (OrderHom.toFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) f (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_2) a₁ a₂)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_2) (OrderHom.toFun.{u1, u1} α α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_2)) f a₁) a₂))
Case conversion may be inaccurate. Consider using '#align order_hom.iterate_sup_le_sup_iff OrderHom.iterate_sup_le_sup_iffₓ'. -/
theorem iterate_sup_le_sup_iff {α : Type _} [SemilatticeSup α] (f : α →o α) :
    (∀ n₁ n₂ a₁ a₂, (f^[n₁ + n₂]) (a₁ ⊔ a₂) ≤ (f^[n₁]) a₁ ⊔ (f^[n₂]) a₂) ↔
      ∀ a₁ a₂, f (a₁ ⊔ a₂) ≤ f a₁ ⊔ a₂ :=
  by
  constructor <;> intro h
  · exact h 1 0
  · intro n₁ n₂ a₁ a₂
    have h' : ∀ n a₁ a₂, (f^[n]) (a₁ ⊔ a₂) ≤ (f^[n]) a₁ ⊔ a₂ :=
      by
      intro n
      induction' n with n ih <;> intro a₁ a₂
      · rfl
      ·
        calc
          (f^[n + 1]) (a₁ ⊔ a₂) = (f^[n]) (f (a₁ ⊔ a₂)) := Function.iterate_succ_apply f n _
          _ ≤ (f^[n]) (f a₁ ⊔ a₂) := (f.mono.iterate n (h a₁ a₂))
          _ ≤ (f^[n]) (f a₁) ⊔ a₂ := (ih _ _)
          _ = (f^[n + 1]) a₁ ⊔ a₂ := by rw [← Function.iterate_succ_apply]

    calc
      (f^[n₁ + n₂]) (a₁ ⊔ a₂) = (f^[n₁]) ((f^[n₂]) (a₁ ⊔ a₂)) :=
        Function.iterate_add_apply f n₁ n₂ _
      _ = (f^[n₁]) ((f^[n₂]) (a₂ ⊔ a₁)) := by rw [sup_comm]
      _ ≤ (f^[n₁]) ((f^[n₂]) a₂ ⊔ a₁) := (f.mono.iterate n₁ (h' n₂ _ _))
      _ = (f^[n₁]) (a₁ ⊔ (f^[n₂]) a₂) := by rw [sup_comm]
      _ ≤ (f^[n₁]) a₁ ⊔ (f^[n₂]) a₂ := h' n₁ a₁ _

#align order_hom.iterate_sup_le_sup_iff OrderHom.iterate_sup_le_sup_iff

end Preorder

end OrderHom


import order.fixed_points order.zorn

universes u v

namespace function
namespace embedding

variable {α : Type u}

theorem schroeder_bernstein {β : Type v} {f : α → β} {g : β → α} :
  injective f → injective g → ∃ H : α → β, bijective H :=
begin
  classical,
  intros hf hg,
  casesI is_empty_or_nonempty β with hβ hβ,
  { have hα := @equiv.equiv_empty α (function.is_empty f),
    have hβ := equiv.equiv_empty β,
    have h := hα.trans hβ.symm,
    exact ⟨h, h.bijective⟩ },
  let F' : set α → set α := λ s, (g '' (f '' s)ᶜ)ᶜ,
  have hF' : monotone F',
  { intros s₁ s₂ h,
    have h := set.image_subset f h,
    rw ←set.compl_subset_compl at h,
    have h := set.image_subset g h,
    rwa ←set.compl_subset_compl at h },
  let F : set α →o set α := ⟨F', hF'⟩,
  let s : set α := order_hom.lfp F,
  have hs : (g '' (f '' s)ᶜ)ᶜ = s := order_hom.map_lfp F,
  have hs : g '' (f '' s)ᶜ = sᶜ := compl_injective (by rw [hs, compl_compl]),
  let g' : α → β := inv_fun g,
  have hg' : left_inverse g' g := left_inverse_inv_fun hg,
  refine ⟨set.piecewise s f g', _, _⟩,
  { rw set.injective_piecewise_iff,
    refine ⟨set.inj_on_of_injective hf s, _, _⟩,
    { intros a₁ ha₁ a₂ ha₂ h,
      obtain ⟨b₁, hb₁, rfl⟩ : a₁ ∈ g '' (f '' s)ᶜ, by rwa hs,
      obtain ⟨b₂, hb₂, rfl⟩ : a₂ ∈ g '' (f '' s)ᶜ, by rwa hs,
      rw [hg', hg'] at h,
      rw h },
    { intros a ha a' ha' h,
      obtain ⟨b, hb, rfl⟩ : a' ∈ g '' (f '' s)ᶜ, by rwa hs,
      rw hg' at h,
      exact hb ⟨a, ha, h⟩ } },
  { rw [←set.range_iff_surjective, set.range_piecewise],
    have h : inv_fun g '' sᶜ = (f '' s)ᶜ,
    { rw [←hs, left_inverse.image_image hg'] },
    rw [h, set.union_compl_self] }
end

def i {β : Type v} (f : α → β) (g : β → α) (n : ℕ) : α → α := (g ∘ f)^[n]

def s {β : Type v} {f g' : α → β} {g f' : β → α}
  (hf : left_inverse f' f) (hg : left_inverse g' g) : set α :=
set_of (λ a, ∃ n a', a = i f g n a' ∧ a' ∉ set.range g)

axiom dec (p : Prop) : decidable p

theorem not_not {p : Prop} : ¬¬p → p :=
decidable.rec (λ h h', false.elim (h' h)) (λ h h', h) (dec p)

lemma hs₁ {β : Type v} {f g' : α → β} {g f' : β → α} {a : α}
  {hf : left_inverse f' f} {hg : left_inverse g' g} :
  a ∉ s hf hg → a ∈ set.range g :=
λ h₁, not_not (λ h₂, h₁ ⟨0, a, rfl, h₂⟩)

lemma iterate_succ' (f : α → α) (n : ℕ) : ∀ a, f^[n + 1] a = f (f^[n] a) :=
nat.rec (λ a, rfl) (λ n ih a, ih (f a)) n

lemma hs₂ {β : Type v} {f g' : α → β} {g f' : β → α} {a₁ a₂ : α}
  {hf : left_inverse f' f} {hg : left_inverse g' g} :
  a₁ ∉ s hf hg → a₂ ∈ s hf hg → g' a₁ ≠ f a₂ :=
begin
  intros ha₁ ha₂ h,
  rcases hs₁ ha₁ with ⟨b, rfl⟩, rename ha₁ hb, rename a₂ a, rename ha₂ ha,
  rw hg at h,
  subst h,
  rcases ha with ⟨n, a, rfl, h⟩,
  refine hb ⟨n + 1, a, _, h⟩, clear hb hf hg f' g' h,
  unfold i,
  rw iterate_succ'
end

lemma hs₃ {β : Type v} {f g' : α → β} {g f' : β → α} {b : β}
  {hf : left_inverse f' f} {hg : left_inverse g' g} :
  g b ∈ s hf hg → f' b ∈ s hf hg ∧ b ∈ set.range f :=
begin
  rintro ⟨n, a, h₁, h₂⟩,
  cases n with n,
  { exact false.elim (h₂ ⟨b, h₁⟩) },
  { unfold i at h₁,
    rw [iterate_succ', ←i] at h₁,
    rw [left_inverse.injective hg h₁, hf],
    exact ⟨⟨n, a, rfl, h₂⟩, ⟨i f g n a, rfl⟩⟩ }
end

theorem schroeder_bernstein' {β : Type v} {f g' : α → β} {g f' : β → α} :
  left_inverse f' f → left_inverse g' g → ∃ H : α → β, bijective H :=
begin
  intros hf hg,
  let s : set α := s hf hg,
  let H : α → β := λ a, @ite β (a ∈ s) (dec (a ∈ s)) (f a) (g' a),
  refine ⟨H, _, _⟩,
  { intros a₁ a₂ h,
    change ite _ _ _ = ite _ _ _ at h,
    clear H,
    cases dec (a₁ ∈ s) with h₁ h₁; cases dec (a₂ ∈ s) with h₂ h₂,
    { rw [@if_neg _ (is_false _) h₁, @if_neg _ (is_false _) h₂] at h,
      replace h₁ := hs₁ h₁,
      replace h₂ := hs₁ h₂,
      rcases h₁ with ⟨b₁, rfl⟩,
      rcases h₂ with ⟨b₂, rfl⟩,
      rw [hg, hg] at h,
      rw h },
    { rw [@if_neg _ (is_false _) h₁, @if_pos _ (is_true _) h₂] at h,
      exact false.elim (hs₂ h₁ h₂ h) },
    { rw [@if_pos _ (is_true _) h₁, @if_neg _ (is_false _) h₂] at h,
      exact false.elim (hs₂ h₂ h₁ h.symm) },
    { exact left_inverse.injective hf h } },
  { intro b,
    change ∃ b', ite _ _ _ = _,
    clear H,
    cases dec (g b ∈ s) with hb hb,
    { exact ⟨g b, by rw [@if_neg _ (dec _) hb, hg]⟩ },
    { refine ⟨f' b, _⟩,
      rcases hs₃ hb with ⟨hb, a, rfl⟩,
      rw [@if_pos _ (dec _) hb, hf] } }
end

theorem antisymm {β : Type v} : (α ↪ β) → (β ↪ α) → nonempty (α ≃ β)
| ⟨f, hf⟩ ⟨g, hg⟩ :=
  let ⟨h, hh⟩ := schroeder_bernstein hf hg in
  ⟨equiv.of_bijective h hh⟩

@[reducible] private def sets (β : α → Type v) : set (set (Π a, β a)) :=
{s : set (Π a, β a) | ∀ (f g ∈ s) a, (f : Π a, β a) a = g a → f = g}

theorem min_injective (β : α → Type v) [hα : nonempty α] :
  ∃ a, nonempty (∀ a', β a ↪ β a') :=
begin
  obtain ⟨S, hS₁, hS₂⟩ : ∃ s ∈ sets β, ∀ s' ∈ sets β, s ⊆ s' → s' = s,
  { refine zorn_subset (sets β) _,
    intros S hS₁ hS₂,
    change ∀ s, _ at hS₁,
    simp only [set.mem_set_of_eq] at hS₁,
    refine ⟨⋃₀ S, _, λ _, set.subset_sUnion_of_mem⟩,
    simp only [set.mem_set_of_eq],
    intros f hf g hg a h,
    rcases hf with ⟨s₁, hs₁, hf⟩,
    rcases hg with ⟨s₂, hs₂, hg⟩,
    cases is_chain.total hS₂ hs₁ hs₂ with h' h',
    { exact hS₁ s₂ hs₂ f (h' hf) g hg a h },
    { exact hS₁ s₁ hs₁ f hf g (h' hg) a h } },
  change ∀ (f g ∈ S) a, (f : Π a, β a) a = g a → f = g at hS₁,
  obtain ⟨a, ha⟩ : ∃ a, ∀ b, ∃ f ∈ S, (f : Π a, β a) a = b,
  { by_contra h,
    push_neg at h,
    obtain ⟨f, hf⟩ : ∃ f : Π a, β a, ∀ a g, g ∈ S → (g : Π a, β a) a ≠ f a,
    { exact classical.axiom_of_choice h },
    suffices h : f ∈ S,
    { exact hf (classical.arbitrary α) f h rfl },
    suffices h : insert f S ∈ sets β,
    { rw ←hS₂ (insert f S) h (set.subset_insert f S),
      exact set.mem_insert f S },
    intros g₁ hg₁ g₂ hg₂,
    rcases hg₁ with rfl | hg₁; rcases hg₂ with rfl | hg₂; intros a ha,
    { refl },
    { exfalso, exact hf a g₂ hg₂ ha.symm },
    { exfalso, exact hf a g₁ hg₁ ha },
    { exact hS₁ g₁ hg₁ g₂ hg₂ a ha } },
  obtain ⟨f, hf⟩ : ∃ f : β a → Π a, β a, ∀ b : β a, f b ∈ S ∧ f b a = b,
  { simpa using classical.axiom_of_choice ha },
  refine ⟨a, ⟨λ a', ⟨_, _⟩⟩⟩,
  { exact λ b, f b a' },
  { intros b₁ b₂ h,
    cases hf b₁ with hfb₁ hb₁,
    cases hf b₂ with hfb₂ hb₂,
    rw [←hb₁, ←hb₂, hS₁ (f b₁) hfb₁ (f b₂) hfb₂ a' h] }
end

theorem total (α : Type u) (β : Type v) : nonempty (α ↪ β) ∨ nonempty (β ↪ α) :=
match min_injective (λ b, cond b (ulift α) (ulift.{max u v} β)) with
| ⟨ff, ⟨f⟩⟩ := or.inr ⟨embedding.congr equiv.ulift equiv.ulift (f tt)⟩
| ⟨tt, ⟨f⟩⟩ := or.inl ⟨embedding.congr equiv.ulift equiv.ulift (f ff)⟩
end

end embedding
end function


import algebra.big_operators.pi
import algebra.module.pi
import algebra.module.linear_map
import algebra.big_operators.ring
import algebra.star.basic
import data.fintype.card
import data.matrix.dmatrix
import linear_algebra.matrix.symmetric
import linear_algebra.matrix
import linear_algebra.matrix.trace
import linear_algebra.matrix.to_lin
import linear_algebra.matrix.nonsingular_inverse
import data.complex.basic
import linear_algebra.eigenspace
import tactic.find
import analysis.normed.group.basic
import analysis.inner_product_space.pi_L2

run_cmd tactic.skip
--set_option trace.simplify true
--set_option trace.simplify.rewrite_failure false
--set_option trace.linarith true

universe u

variables {α n m R : Type*} [fintype n] [fintype m] [decidable_eq n] [has_neg α]

namespace matrix

open_locale matrix

open_locale complex_order complex_conjugate

instance : has_coe (n → ℝ) (n → ℂ) := ⟨λ x, (λ i, ⟨x i, 0⟩)⟩
instance : has_coe (matrix m n ℝ) (matrix m n ℂ) := ⟨λ M, (λ i j, ⟨M i j, 0⟩)⟩

-- The conjugate of a vector is obtained by taking the conjugate of each element of the vector
def vector_conj (v : n → ℂ) : n → ℂ := λ i : n, conj (v i)

-- The matrix conjugate is obtained by taking the conjugate of each element of the matrix
def matrix_conj (M : matrix n n ℂ) : matrix n n ℂ := λ i : n, vector_conj (M i)

-- An eigenpair of M is a pair (e, v) such that v ≠ 0 and M v = e v
def has_eigenpair (M : matrix n n ℂ) (e : ℂ) (v : n → ℂ ) := v ≠ 0 ∧ mul_vec M v = e • v

-- A vector which is part of an eigenpair of M is called an eigenvector of M
def has_eigenvector (M : matrix n n ℂ) (v : n → ℂ ) := ∃ e : ℂ , has_eigenpair M e v

-- A number which is part of an eigenpair of M is called an eigenvalue of M
def has_eigenvalue (M : matrix n n ℂ) (e : ℂ) := ∃ v : n → ℂ , has_eigenpair M e v

-- A left eignpair of M is a pair (e, v) such that v ≠ 0 and vᵀ M = vᵀ e
def has_left_eigenpair (M : matrix n n ℂ) (e : ℂ) (v : n → ℂ ) := v ≠ 0 ∧ vec_mul v M = e • v

-- A vector which is part of an eigenpair of M is called a left eigenvector of M
def has_left_eigenvector (M : matrix n n ℂ) (v : n → ℂ ) := ∃ e : ℂ , has_left_eigenpair M e v

-- A number which is part of an eigenpair of M is called a left eigenvalue of M
def has_left_eigenvalue (M : matrix n n ℂ) (e : ℂ) := ∃ v : n → ℂ , has_left_eigenpair M e v

-- A matrix is caleed skew-symmetric if its transpose is equal to its negative
def is_skew_symmetric (M : matrix n n α ) := Mᵀ = -M

-- ∑ Re(f (v i)) = Re(∑ f (v i))
lemma sum_re {v : n → ℂ} {f : ℂ → ℂ }: finset.univ.sum (λ i, (f (v i)).re) = (finset.univ.sum (λ i, f(v i))).re:=
begin
  rw ← complex.re_lm_coe,
  rw linear_map.map_sum,
end

-- ∑ Im(f (v i)) = Im(∑ f (v i))
lemma sum_im {v : n → ℂ} {f : ℂ → ℂ }: finset.univ.sum (λ i, (f (v i)).im) = (finset.univ.sum (λ i, f(v i))).im:=
begin
  rw ← complex.im_lm_coe,
  rw linear_map.map_sum,
end

-- ∑ (f (v i))' = (∑ f (v i))'
lemma sum_conj_func {v : n → ℂ } {f : ℂ → ℂ }: conj (finset.univ.sum (λ i, f (v i))) = finset.univ.sum (λ i, conj (f (v i))) :=
begin
  ext,
  rw complex.conj_re,
  rw ← sum_re,
  change finset.univ.sum (λ (i : n), (conj (f (v i))).re) = (finset.univ.sum (λ (i : n), conj (f (v i)))).re,
  rw sum_re,
  rw ← sum_im,
  simp only [complex.conj_im, finset.sum_neg_distrib, neg_inj],
  rw sum_im,
end

-- ∑ (v i)' = (∑ (v i))'
lemma sum_conj {v : n → ℂ} : conj (finset.univ.sum (λ i, (v i))) = finset.univ.sum (λ i, conj ((v i))) :=
begin

  have h : conj (finset.univ.sum (λ i, id (v i))) = finset.univ.sum (λ i, conj (id (v i))),
  {exact sum_conj_func},

  dsimp at h,
  exact h,
end

-- (a * b)' = a' * b'
lemma mul_conj (a : ℂ ) (b : ℂ ) : conj (a * b) = (conj a) * (conj b) :=
begin
  ext,
  simp only [complex.mul_re, complex.conj_re, complex.conj_im, mul_neg, neg_mul, neg_neg],
  simp only [complex.mul_im, neg_add_rev, complex.conj_re, complex.conj_im, mul_neg, neg_mul],
  rw add_comm,
end

-- (vᵀv)' = ((v')ᵀv')
lemma dot_product_conj (v : n → ℂ) (w : n → ℂ) : conj (dot_product v w) = dot_product (vector_conj v) (vector_conj w) :=
begin
  repeat {rw dot_product},
  repeat {rw vector_conj},
  dsimp only,
  rw sum_conj,
  apply finset.sum_congr,
  refl,
  intros _ _,
  rw mul_conj,
end

-- (vᵀM)' = (v')ᵀM'
lemma vec_mul_conj (v : n → ℂ) (M : matrix n n ℂ) : vector_conj (vec_mul v M) = vec_mul (vector_conj v) (matrix_conj M):=
begin

  have vm : vec_mul (vector_conj v) M.matrix_conj = λ i , dot_product (vector_conj v) (λ j, conj (M j i)),
  {
    ext;
    rw vec_mul;
    rw dot_product;
    rw dot_product;
    rw matrix_conj;
    simp only;
    rw vector_conj;
    simp only;
    simp_rw vector_conj
  },

  rw vm,
  ext;
  rw vector_conj;
  rw vector_conj;
  dsimp only;
  rw vec_mul;
  rw dot_product_conj;
  repeat {rw vector_conj},
end

-- (Mv)' = M'v'
lemma mul_vec_conj (v : n → ℂ) (M : matrix n n ℂ) : vector_conj (mul_vec M v) = mul_vec (matrix_conj M) (vector_conj v) :=
begin
  have mv : mul_vec (matrix_conj M) (vector_conj v) = λ i , dot_product (vector_conj v) (λ j, conj (M i j)),
  {
    ext;
    rw mul_vec;
    rw dot_product;
    rw dot_product;
    rw matrix_conj;
    simp only;
    rw vector_conj;
    simp only;
    simp_rw mul_comm
  },

  rw mv,
  ext;
  rw vector_conj;
  rw vector_conj;
  dsimp only;
  rw mul_vec;
  rw dot_product_conj;
  repeat {rw vector_conj};
  simp_rw dot_product;
  simp_rw mul_comm,
end

-- v'' = v
lemma vector_conj_conj (v : n → ℂ) : vector_conj (vector_conj v) = v :=
begin
  apply funext,
  rw vector_conj,
  intro x,
  rw vector_conj,
  change conj (conj (v x)) = v x,
  rw complex.conj_conj,
end

-- M'' = M
lemma matrix_conj_conj (M : matrix n n ℂ) : matrix_conj (matrix_conj M) = M :=
begin
  apply funext,
  rw matrix_conj,
  rw matrix_conj,
  intro x,
  change vector_conj (vector_conj (M x)) = M x,
  rw vector_conj_conj,
end

-- (x • v)' = x' • v'
lemma vec_smul_conj (v : n → ℂ) (x : ℂ) : vector_conj (x • v) = (conj x) • (vector_conj v) :=
begin
  repeat {rw vector_conj},
  simp only [pi.smul_apply, algebra.id.smul_eq_mul, map_mul],
  simp_rw mul_conj,
  refl,
end

-- (x • M)' = x' • M'
lemma mat_smul_conj (M : matrix n n ℂ) (x : ℂ) : matrix_conj (x • M) = (conj x) • (matrix_conj M):=
begin
  repeat {rw matrix_conj},
  funext,
  dsimp,
  rw vec_smul_conj,
  rw pi.smul_apply,
  rw algebra.id.smul_eq_mul,
end

-- (M + N)' = M' + N'
lemma mat_add_conj (M : matrix n n ℂ) (N : matrix n n ℂ ) : matrix_conj (M + N) = matrix_conj M + matrix_conj N :=
begin
  funext,
  repeat {rw matrix_conj},
  dsimp,
  repeat {rw vector_conj},
  dsimp,
  rw map_add,
end

-- (M - N)' = M' - N'
lemma mat_sub_conj (M : matrix n n ℂ) (N : matrix n n ℂ ) : matrix_conj (M - N) = matrix_conj M - matrix_conj N :=
begin
  funext,
  repeat {rw matrix_conj},
  dsimp,
  repeat {rw vector_conj},
  dsimp,
  rw map_sub,
end

-- M' i j = (M i j)'
lemma mat_conj_apply (M : matrix n n ℂ) (i j : n): M.matrix_conj i j = (conj (M i j)) :=
begin
  simp_rw matrix_conj,
  rw vector_conj,
end

-- (M ⬝ N)' = M' ⬝ N'
lemma mat_mul_conj (M N : matrix n n ℂ) : (M ⬝ N).matrix_conj = M.matrix_conj ⬝ N.matrix_conj :=
begin
  funext,
  rw matrix.mul_apply,
  rw mat_conj_apply,
  rw matrix.mul,
  simp_rw dot_product,
  rw sum_conj,
  simp_rw mul_conj,
  simp_rw mat_conj_apply,
end

-- (Mᵀv) = (vM)ᵀ
lemma mul_vec_trans (M : matrix n n ℂ ) (v : n → ℂ ) : (mul_vec Mᵀ v) = (vec_mul v M) :=
begin
  ext;
  rw mul_vec;
  rw vec_mul;
  rw dot_product;
  rw dot_product;
  simp_rw matrix.transpose;
  rw finset.sum_congr,
  refl,
  intros _ _,
  rw mul_comm,
  refl,
  intros _ _,
  rw mul_comm,
end

-- vᵀMᵀ = (Mv)ᵀ
lemma vec_mul_trans (M : matrix n n ℂ) (v : n → ℂ ) :  (vec_mul v Mᵀ ) = mul_vec M v :=
begin
  have h : M.mul_vec v = Mᵀᵀ.mul_vec v,
  rw transpose_transpose,
  rw h,
  rw mul_vec_trans,
end

-- (M - N)v = Mv - Nv
lemma sub_mul_vec (M N : matrix n n ℂ) (v : n → ℂ ) : (M - N).mul_vec v = M.mul_vec v - N.mul_vec v:=
begin
  repeat {rw sub_eq_add_neg},
  rw add_mul_vec,
  rw add_right_inj,
  funext,
  repeat{rw mul_vec},
  simp only [pi.neg_apply],
  rw mul_vec,
  simp_rw dot_product,
  rw ← finset.sum_neg_distrib,
  rw finset.sum_congr,
  refl,
  intros _ _,
  rw neg_mul,
end

-- Mᵀ' = M'ᵀ
lemma matrix_trans_conj (M : matrix n n ℂ) : (matrix_conj Mᵀ) = (matrix_conj M)ᵀ :=
begin
  funext,
  rw matrix_conj,
  rw transpose_apply,
  dsimp,
  rw vector_conj,
  rw matrix_conj,
  dsimp,
  rw vector_conj,
end

-- (M ⬝ N)ᵀ = Nᵀ ⬝ Mᵀ
lemma matrix_mul_trans (M N : matrix n n ℂ) : (M ⬝ N)ᵀ = Nᵀ ⬝ Mᵀ :=
begin
  funext i j,
  rw transpose_apply,
  repeat {rw matrix.mul},
  dsimp,
  rw dot_product_comm,
  refl,
end

-- (M ⬝ Mᵀ) = (M ⬝ Mᵀ)ᵀ
lemma prod_transpose_symm (M : matrix n n ℂ) : (M ⬝ Mᵀ)=(M ⬝ Mᵀ)ᵀ :=
begin
  rw eq_comm,
  rw matrix_mul_trans,
  rw transpose_transpose,
end

noncomputable def vector_sq_mag (v : n → ℂ) : ℂ := (dot_product (vector_conj v) v)

-- (v')ᵀv ≥ 0
lemma vector_sq_mag_geq_zero (v : n → ℂ) : vector_sq_mag v ≥ 0 :=
begin
   rw vector_sq_mag,
   rw dot_product,

   -- ∀ i, (v' i) (v i) ≥ 0
   have h : ∀ (x : n), conj (v x) * (v x) ≥ 0,
   {
     intro x,

     have h_im : (conj (v x) * (v x)).im = (0:ℂ).im,
     {
       rw complex.mul_im,
       rw complex.conj_re,
       rw complex.conj_im,
       rw complex.zero_im,
       nlinarith, -- Re(v x) * Im(v x) + - Im(v x) * Re(v x) = 0
     },

    have h_re : (conj (v x) * (v x)).re ≥ (0:ℂ).re,
    {
      rwa complex.mul_re,
      rwa complex.conj_re,
      rwa complex.conj_im,
      rw neg_mul,
      rw sub_neg_eq_add,
      rw complex.zero_re,
      nlinarith,  -- Re(v x) * Re(v x) + Im(v x) * Im(v x) ≥ 0
    },

    rw [ge,complex.le_def],
    split,
    assumption, -- h_re
    rw eq_comm,
    assumption, -- h_im
   },

  apply finset.sum_nonneg,  -- ∀ i, 0 ≤ (v' i) * (v i)
  intro x,
  intro _,
  specialize h x,
  rw ← ge,
  exact h,
end

-- (v')ᵀv ∈ ℝ
lemma vector_sq_mag_real (v : n → ℂ ) : vector_sq_mag v = (vector_sq_mag v).re :=
begin
  ext,
  rw complex.of_real_re,

  have h := vector_sq_mag_geq_zero v,

  rw [ge,complex.le_def] at h,
  cases h with h_re h_im,
  rw complex.of_real_im,
  rw ← h_im,
  rw complex.zero_im,
end

-- (v')ᵀv = 0 ↔ v = 0
theorem vector_zero_iff_sq_mag_zero (v : n → ℂ) : vector_sq_mag v = 0 ↔ v=0 :=
begin
  rw vector_sq_mag,
  split,
  intro prod,
  rw dot_product at prod,

  -- (v i) * (v' i) ≥ 0
  have gez : ∀ i : n, v i * vector_conj v i ≥ 0,
  {
    intro i,
    rw vector_conj,
    dsimp,
    rw ge_iff_le,
    rw complex.le_def,
    split,
    rw complex.mul_re,
    simp only [complex.zero_re, complex.conj_re, complex.conj_im, mul_neg, sub_neg_eq_add],
    nlinarith,  -- 0 ≤ Re(v i) * Re(v i) + Im(v i) * Im(v i)
    rw complex.mul_im,
    rw complex.conj_im,
    rw complex.conj_re,
    simp only [complex.zero_im, mul_neg],
    rw mul_comm,
    rw add_left_neg,
  },

  have important : finset.univ.sum(λ i , v i * vector_conj v i ) = 0 ↔ ∀ i ∈ finset.univ, v i * vector_conj v i = 0,
  {
    apply finset.sum_eq_zero_iff_of_nonneg, -- ∀ i, 0 ≤ (v i) * (v' i)
    intros i _,
    specialize gez i,
    rw ge_iff_le at gez,
    exact gez
  },

  simp_rw mul_comm at prod,
  rw prod at important,
  simp only [eq_self_iff_true, finset.mem_univ, mul_eq_zero, forall_true_left, true_iff] at important,
  funext i,
  specialize important i,
  cases important with vz vcz,
  rw vz,
  refl,
  rw vector_conj at vcz,
  dsimp at vcz,

  have vczc := congr_arg conj vcz,

  rw [complex.conj_conj] at vczc,
  simp only [map_zero] at vczc,
  rw vczc,
  refl,
  intro ez,
  rw ez,
  rw dot_product_comm,
  rw zero_dot_product,
end

-- |x • v| = x²|v|
lemma smul_sq_mag (x : ℂ) (v: n → ℂ ) (h : x.im = 0): vector_sq_mag (x • v) = (x * x) * vector_sq_mag v :=
begin
  rw vector_sq_mag,
  rw vec_smul_conj,

  simp only [dot_product_smul, smul_dot_product, algebra.id.smul_eq_mul, monoid_with_zero_hom.coe_mk, complex.of_real_add,
  complex.of_real_mul],
  rw ← vector_sq_mag,
  rw ← mul_assoc,

  rw star_ring_end_apply,

  suffices x_star : star x = x,
  {rw x_star},

  unfold star,
  ext,
  refl,
  rw h,
  rw neg_zero,
end

-- A vector is real iff it is equal to its conjugate
lemma vector_eq_conj_iff_real (v : n → ℂ) : (∃ u : n → ℝ, v = u) ↔ v = vector_conj v :=
begin
  split,
  intro h,
  cases h with u veq,
  funext,
  rw vector_conj,
  dsimp,
  rw veq,
  rw eq_comm,
  rw complex.eq_conj_iff_real,
  use u x,
  refl,
  intro veq,
  use (λ i , (v i).re ),
  funext,

  have vx_re : v x = (v x).re,
  {
    ext,
    rw complex.of_real_re,
    rw complex.of_real_im,
    rw ← add_self_eq_zero,
    nth_rewrite 0 veq,
    rw vector_conj,
    dsimp,
    rw add_left_neg
  },

  rw vx_re,
  refl,
end

-- A matrix is real iff it is equal to its conjugate
lemma matrix_eq_conj_iff_real (M : matrix n n ℂ) : (∃ N : matrix n n ℝ, M = N) ↔ M = matrix_conj M :=
begin
  split,
  intro h,
  cases h with N eq,
  rw eq,
  funext,
  rw matrix_conj,
  simp_rw vector_conj,
  rw eq_comm,
  rw complex.eq_conj_iff_real,
  use N i j,
  refl,

  intro meq,
  use (λ (i_1 i_2 : n), (M i_1 i_2).re),
  funext i j,

  have mij_conj : M i j = conj (M i j),
  {
    nth_rewrite 0 meq,
    rw matrix_conj,
    dsimp,
    rw vector_conj,
  },

  have mij_re : M i j = (M i j).re,
  {
    ext,
    rw complex.of_real_re,
    rw complex.of_real_im,
    rw ← add_self_eq_zero,
    nth_rewrite 0 mij_conj,
    rw complex.conj_im,
    rw add_left_neg
  },

  rw eq_comm at mij_conj,
  rw mij_re,
  refl,
end

-- The sum of a matrix and its conjugate is a real matrix
lemma matrix_conj_add_real (M : matrix n n ℂ) : ∃ N : matrix n n ℝ , (matrix_conj M + M) = N :=
begin
  rw matrix_eq_conj_iff_real,
  rw mat_add_conj,
  rw matrix_conj_conj,
  rw add_comm,
end

section hermitian

-- A matrix is called Hermitian if it is equal to the conjugate of its transpose
def is_hermitian (M : matrix n n ℂ) :=  matrix_conj Mᵀ  = M

-- The sum of two Hermitian matrices is Hermitian
lemma herm_sum (M : matrix n n ℂ) (N : matrix n n ℂ) (hm : is_hermitian M) (hn : is_hermitian N) : is_hermitian (M + N) :=
begin
  rw is_hermitian,
  rw is_hermitian at hm,
  rw is_hermitian at hn,
  funext,
  rw matrix_conj,
  dsimp,
  rw vector_conj,
  dsimp,
  nth_rewrite 1 ← hm,
  nth_rewrite 1 ← hn,
  repeat {rw matrix_trans_conj},
  dsimp,
  repeat {rw matrix_conj},
  dsimp,
  repeat {rw vector_conj},
  dsimp,
  rw map_add,
end

-- A Hermitian matrix only has real eigenvalues
theorem hermitian_real_eigenvalues (M : matrix n n ℂ) (h : is_hermitian M) (e : ℂ) (ev : has_eigenvalue M e) : e.im = 0 :=
begin
  rw has_eigenvalue at ev,
  cases ev with v ep,
  rw has_eigenpair at ep,
  cases ep with nz mul,
  rw is_hermitian at h,

  have mul_0 : dot_product (vector_conj v) (M.mul_vec v) = dot_product (vector_conj v) (e • v),
  {
    rw mul,
  },

  have mul_1 : dot_product (vector_conj v) (e • v) = e * (vector_sq_mag v),
  {
    rw vector_sq_mag,
    simp only [dot_product_smul, algebra.id.smul_eq_mul],
  },

  have mul_2 : dot_product v (e • (vector_conj v)) = e * vector_sq_mag v,
  {
    rw dot_product_comm,
    rw vector_sq_mag,
    rw dot_product,
    rw dot_product,
    rw finset.mul_sum,
    rw finset.sum_congr,
    refl,
    intros _ _,
    simp only [pi.smul_apply, algebra.id.smul_eq_mul,mul_assoc],
  },

  have mul_3 : dot_product (vector_conj v) (mul_vec (Mᵀ.matrix_conj) v) = (conj e) * vector_sq_mag v,
  {
    rw dot_product_mul_vec,
    rw matrix_trans_conj,
    rw vec_mul_trans,
    rw ← mul_vec_conj,
    rw mul,
    rw vec_smul_conj,
    rw vector_sq_mag,
    simp only [smul_dot_product, algebra.id.smul_eq_mul],
  },

  rw h at mul_3,
  rw mul at mul_3,
  rw mul_1 at mul_3,
  rw ne at nz,
  rw ← vector_zero_iff_sq_mag_zero at nz,
  rw vector_sq_mag at nz,
  rw ← vector_sq_mag at nz,
  rw mul_eq_mul_right_iff at mul_3,
  simp only [nz] at mul_3,
  rw or_false at mul_3,

  have e_im := congr_arg complex.im mul_3,

  rw complex.conj_im at e_im,
  rw eq_neg_iff_add_eq_zero at e_im,
  rw add_self_eq_zero at e_im,
  exact e_im,
end

-- M is Hermitian iff -M is Hermitian
theorem herm_iff_neg_herm (M : matrix n n ℂ) : is_hermitian M ↔ is_hermitian (-M) :=
begin
  repeat {rw is_hermitian},

  have key : (-M)ᵀ.matrix_conj = -(Mᵀ.matrix_conj),
  {
    funext,
    rw matrix_conj,
    rw matrix_conj,
    dsimp,
    rw vector_conj,
    rw vector_conj,
    dsimp,
    ext,
    simp only [complex.neg_re, complex.conj_re],
    simp only [complex.neg_im, complex.conj_im]
  },

  rw key,
  rw neg_inj,
end

-- A Hermitian matrix multiplied by a non-zero real number is Hermitian
theorem herm_real_smul_herm (M : matrix n n ℂ) (a : ℂ) : a=a.re → (a=0 ∨ is_hermitian M ↔ is_hermitian (a • M)):=
begin
  intro a_re,
  rw is_hermitian,
  rw is_hermitian,
  rw transpose_smul,
  rw mat_smul_conj Mᵀ a,
  nth_rewrite 1 a_re,
  simp only [is_R_or_C.conj_of_real, complex.coe_smul],
  split,
  intro h,
  cases h with az m_herm,
  rw az,
  rw zero_smul,
  rw complex.zero_re,
  rw zero_smul,
  rw m_herm,
  rw a_re,
  simp only [complex.of_real_re, complex.coe_smul],
  intro h,
  rw or_iff_not_and_not,
  by_contra h_1,
  cases h_1 with anz mnh,
  apply mnh,
  rw ← sub_eq_zero at h,
  rw a_re at h,
  simp only [complex.of_real_re, complex.coe_smul] at h,
  rw ← smul_sub at h,
  rw smul_eq_zero at h,
  rw a_re at anz,
  rw complex.of_real_eq_zero at anz,
  simp only [anz, false_or] at h,
  rw sub_eq_zero at h,
  exact h,
end

-- Every matrix can be written in the form A + iB, where A and B are Hermitian matrices
theorem matrix_decomposition_hermitian (M : matrix n n ℂ) : ∃ A : matrix n n ℂ, ∃ B : matrix n n ℂ, is_hermitian A ∧ is_hermitian B ∧ M = A + complex.I • B :=
begin
  have h := herm_real_smul_herm,

  have trivial : (1/2) = ((complex.re(1/2)):ℂ),
  {
    ext,
    rw complex.of_real_re,
    rw complex.of_real_im,
    simp only [one_div, complex.inv_im, complex.bit0_im, complex.one_im, bit0_zero, neg_zero', zero_div],
  },

  have trivial_2 : ((1:ℂ)/2) = 0 = false,
  {
    simp only [eq_iff_iff, iff_false],
    by_contra,
    simp only [one_div, inv_eq_zero, bit0_eq_zero, one_ne_zero] at h,
    exact h,
  },

  have trivial_3 : (1:ℂ)/2 + (1:ℂ)/2 = 1,
  {
    rw ← sub_eq_zero,
    rw ← two_mul,
    rw mul_div_comm,
    rw div_self,
    rw mul_one,
    rw sub_self,
    by_contra,
    rw ← one_add_one_eq_two at h,
    rw add_self_eq_zero at h,

    have h_1 := one_ne_zero,

    rw ne at h_1,
    apply h_1,
    exact h,
    exact infinite.nontrivial ℂ,
  },

  use ((1:ℂ)/2) • (M + (matrix_conj Mᵀ)),
  use (complex.I/2) • ((matrix_conj Mᵀ) - M),

  split,
  rw ← herm_real_smul_herm,
  rw trivial_2,
  rw false_or,
  rw is_hermitian,
  rw transpose_add,
  rw mat_add_conj,
  nth_rewrite 1 add_comm,
  rw add_left_cancel_iff,
  rw matrix_trans_conj,
  rw matrix_conj_conj,
  rw transpose_transpose,
  exact trivial,
  split,

  rw is_hermitian,
  rw smul_sub,
  rw matrix_trans_conj,
  rw mat_sub_conj,
  rw matrix_trans_conj,
  rw mat_smul_conj,
  simp only [transpose_sub, transpose_smul],
  rw matrix_trans_conj,
  rw transpose_transpose,
  rw matrix_conj_conj,

  have conj_i : conj(complex.I/2) = -complex.I/2,
  {
    ext,
    rw complex.conj_re,
    rw neg_div,
    rw complex.neg_re,
    rw eq_neg_iff_add_eq_zero,
    rw add_self_eq_zero,
    rw complex.div_re,
    rw complex.I_re,
    simp only [zero_mul, zero_div, complex.bit0_im, complex.one_im, bit0_zero, mul_zero, add_zero],
    rw complex.conj_im,
    rw neg_div,
    rw complex.neg_im,
  },

  rw conj_i,
  rw neg_div,
  nth_rewrite 1 ← neg_add_eq_sub,
  rw neg_smul,
  rw sub_eq_add_neg,
  rw add_right_inj,
  rw mat_smul_conj,
  rw conj_i,
  rw transpose_smul,
  rw neg_div,
  rw neg_smul,
  rw neg_neg,

  rw smul_add,
  rw ← smul_assoc,
  rw smul_sub,
  rw smul_eq_mul,
  rw mul_div,
  rw complex.I_mul_I,
  rw neg_div,
  rw neg_smul,
  rw neg_smul,
  rw neg_sub_neg,
  rw add_add_sub_cancel,
  rw ← add_smul,
  rw trivial_3,
  rw one_smul,
  exact n,
  exact _inst_1,
  exact _inst_3,
end

-- Every Hermitian matrix can be written as A + iB, where A is a symmetric real matrix and B is a skew-symmetric real matrix
theorem matrix_decomposition_symm_skew_symm (M : matrix n n ℂ) (herm : is_hermitian M) : ∃ A : matrix n n ℝ , ∃ B : matrix n n ℝ, is_symm A ∧ is_skew_symmetric B ∧ M = A + complex.I • B:=
begin

  have mul_2_scal: ∀ (x y : ℂ) , x = y ↔ (2 : ℂ)*x = (2 : ℂ)*y,
  {
    intros x y,
    split,
    intro xey,
    rw xey,

    intro xey_2,

    rw mul_eq_mul_left_iff at xey_2,
    cases xey_2 with xey f,
    exact xey,
    exfalso,
    rw ← one_add_one_eq_two at f,
    rw add_self_eq_zero at f,

    have onz := one_ne_zero,

    rw ne at onz,
    apply onz,
    exact f,
    exact infinite.nontrivial ℂ,
  },

  have mul_2 : ∀ (X Y : matrix n n ℂ) , X = Y ↔ (2 : ℂ) • X = (2 : ℂ) • Y,
  {
    intros X Y,
    split,
    intro xey,
    rw xey,
    intro xey_2,
    funext,
    repeat{rw two_smul at xey_2},

    have mul_2_scalar : (X + X) x x_1 = (Y+Y) x x_1,
    {rw xey_2},

    simp only [dmatrix.add_apply] at mul_2_scalar,
    repeat {rw ← two_mul at mul_2_scalar},
    rw mul_eq_mul_left_iff at mul_2_scalar,
    simp only [bit0_eq_zero, one_ne_zero, or_false] at mul_2_scalar,
    exact mul_2_scalar,
  },

  have complex_two_nez : (2:ℂ) ≠ (0:ℂ),
  {
    rw ← one_add_one_eq_two,
    rw ne.def,
    rw add_self_eq_zero,
    rw ← ne.def,
    exact one_ne_zero,
  },

  have two_inv_real : (2:ℂ)⁻¹.re = (2:ℝ)⁻¹,
  {
    simp only [complex.inv_re, complex.bit0_re, complex.one_re],
    rw eq_comm,
    apply eq_div_of_mul_eq,
    simp only [ne.def, monoid_with_zero_hom.map_eq_zero, bit0_eq_zero, one_ne_zero, not_false_iff],
    rw complex.norm_sq,
    dsimp,
    simp only [complex.bit0_im, complex.one_im, bit0_zero, mul_zero, add_zero, inv_mul_cancel_left₀, ne.def, bit0_eq_zero,
    one_ne_zero, not_false_iff],
  },

  have two_inv_im : (2:ℂ)⁻¹.im = (0:ℝ),
  {
    rw complex.inv_im,
    simp only [complex.bit0_im, complex.one_im, bit0_zero, neg_zero', zero_div],
  },

  have h_1 : ((1:ℂ)/2) • (matrix_conj M + M) = matrix_conj (((1:ℂ)/2) • (matrix_conj M + M)),
  {
    rw mat_smul_conj,
    rw mat_add_conj,
    rw matrix_conj_conj,
    rw add_comm,

    suffices c12 : (1:ℂ)/2 = conj ((1:ℂ)/2),
    {rw ← c12},

    rw one_div,
    rw eq_comm,
    rw complex.eq_conj_iff_im,
    exact two_inv_im,
  },

  have conj_i : conj(complex.I/2) = -complex.I/2,
  {
    ext,
    rw complex.conj_re,
    rw neg_div,
    rw complex.neg_re,
    rw eq_neg_iff_add_eq_zero,
    rw add_self_eq_zero,
    rw complex.div_re,
    rw complex.I_re,
    simp only [zero_mul, zero_div, complex.bit0_im, complex.one_im, bit0_zero, mul_zero, add_zero],
    rw complex.conj_im,
    rw neg_div,
    rw complex.neg_im,
  },

  have g_1 : (complex.I/(2:ℂ)) • (matrix_conj M - M) = matrix_conj ((complex.I/(2:ℂ)) • (matrix_conj M - M)),
  {
    rw mat_smul_conj,
    rw mat_sub_conj,
    rw matrix_conj_conj,
    simp_rw smul_sub,
    rw conj_i,
    repeat {rw neg_div},
    repeat {rw neg_smul},
    rw neg_sub_neg,
  },

  rw ← matrix_eq_conj_iff_real at h_1,
  cases h_1 with N hN,
  rw ← matrix_eq_conj_iff_real at g_1,
  cases g_1 with O hO,

  existsi N,
  existsi O,
  rw is_hermitian at herm,

  split,

  rw is_symm,

  have h_2 : Nᵀ = N,
  {
    ext,
    rw ← complex.of_real_inj,

    have h_3 : ∀ (i_1 i_2 : n ), (N:matrix n n ℂ) i_1 i_2 = ((N i_1 i_2):ℂ),
    intros i_1 i_2,
    refl,

    have n_ji : (N:matrix n n ℂ) j i = ((N j i):ℂ),
    specialize h_3 j i,
    rw h_3,

    have n_ij : (N:matrix n n ℂ) i j = ((N i j):ℂ),
    specialize h_3 i j,
    rw h_3,

    rw ← n_ji,
    rw ← n_ij,

    rw ← hN,
    dsimp,
    nth_rewrite 0 ← herm,
    rw matrix_conj_conj,
    rw transpose,
    nth_rewrite 2 ← herm,
    rw matrix_conj_conj,
    rw transpose,
    rw add_comm,
  },

  have mul_3 := mul_2,

  specialize mul_2 M,
  specialize mul_2 (↑(((1:ℝ) / 2) • N) + -(((1:ℂ) / 2) • M.matrix_conj) + ((1:ℂ) / 2) • M),

  have n_smul : (N:(matrix n n ℂ)) = (2:ℂ) • ( ((1:ℂ)/2) • (N:(matrix n n ℂ))),
  {simp only [one_div, smul_inv_smul₀, ne.def, bit0_eq_zero, one_ne_zero, not_false_iff]},

  rw h_2,

  have g_3 : Oᵀ = -O,
  {
    ext,
    rw transpose_apply,
    repeat{rw pi.neg_apply},
    rw ← complex.of_real_inj,
    rw complex.of_real_neg,

    have h_3 : ∀ (i_1 i_2 : n ), (O:matrix n n ℂ) i_1 i_2 = ((O i_1 i_2):ℂ),
    intros i_1 i_2,
    refl,

    have o_ji : (O:matrix n n ℂ) j i = ((O j i):ℂ),
    specialize h_3 j i,
    rw h_3,

    have o_ij : (O:matrix n n ℂ) i j = ((O i j):ℂ),
    specialize h_3 i j,
    rw h_3,

    rw ← o_ji,
    rw ← o_ij,
    repeat {rw ← hO},

    simp only [pi.smul_apply, pi.sub_apply, algebra.id.smul_eq_mul],
    repeat {rw mul_sub},
    rw neg_sub,
    nth_rewrite 0 ← herm,
    nth_rewrite 3 ← herm,
    repeat {rw matrix_conj_conj},
    repeat {rw transpose_apply},
  },

  split,
  rw is_skew_symmetric,
  rw g_3,
  rw ← hO,
  rw ← hN,
  rw smul_add,
  rw ← smul_assoc,
  rw smul_eq_mul,
  rw ← mul_div_assoc,
  rw complex.I_mul_I,
  rw neg_div,
  rw smul_sub,
  simp only [one_div, neg_smul, neg_sub_neg],
  rw add_sub,
  rw add_assoc,
  rw add_sub_cancel',
  rw mul_2,
  simp only [smul_add, smul_inv_smul₀, ne.def, bit0_eq_zero, one_ne_zero, not_false_iff],
  rw ← one_add_one_eq_two,
  rw add_smul,
  repeat {rw one_smul},
end

-- A Hermitian real matrix is symmetric
lemma hermitian_real_symm  (M : matrix n n ℝ) (herm : is_hermitian (M:matrix n n ℂ)) : M.is_symm:=
begin
  rw is_hermitian at herm,
  rw is_symm,

  have herm_1 : ∀ (i j : n), ((M : matrix n n ℂ)ᵀ.matrix_conj) i j = M i j,
  {
    intros i j,
    rw herm,
    refl
  },

  funext i j,
  specialize herm_1 i j,
  rw matrix_trans_conj at herm_1,
  rw transpose_apply at herm_1,
  rw matrix_conj at herm_1,
  simp_rw vector_conj at herm_1,

  unfold coe at herm_1,
  unfold lift_t at herm_1,
  unfold has_lift_t.lift at herm_1,
  unfold coe_t at herm_1,
  unfold has_coe_t.coe at herm_1,
  unfold coe_b at herm_1,
  unfold has_coe.coe at herm_1,

  have trivial : ((conj (({re := M j i, im := 0}):ℂ)):ℂ) = (({re := M j i, im := 0}):ℂ),
  {
    ext,
    rw complex.conj_re,
    rw complex.conj_im,
    unfold complex.im,
    rw neg_zero'
  },

  rw trivial at herm_1,
  rw transpose_apply,

  have trivial_re := congr_arg complex.re herm_1,

  unfold complex.re at trivial_re,
  exact trivial_re,
end

section definite

-- A Hermitian matrix is called positive definite if the product vᵀMv is greater than 0 for all non-zero vectors v
def is_pos_def (M : matrix n n ℂ) := M.is_hermitian ∧ ∀ (v : n → ℂ), v ≠ 0 → 0 < dot_product (vector_conj v) (M.mul_vec v)

-- A Hermitian matrix is called negative definite if the product vᵀMv is smaller than 0 for all non-zero vectors v
def is_neg_def (M : matrix n n ℂ) := M.is_hermitian ∧ ∀ (v : n → ℂ), v ≠ 0 → 0 > dot_product (vector_conj v) (M.mul_vec v)

-- A Hermitian matrix is called positive semidefinite if the product vᵀMv is greater than or equal to 0 for all non-zero vectors v
def is_pos_semidef (M : matrix n n ℂ) := M.is_hermitian ∧ ∀ (v : n → ℂ), v ≠ 0 → 0 ≤ dot_product (vector_conj v) (M.mul_vec v)

-- -- A Hermitian matrix is called negative semidefinite if the product vᵀMv is greater than 0 for all non-zero vectors v
def is_neg_semidef (M : matrix n n ℂ) := M.is_hermitian ∧ ∀ (v : n → ℂ), v ≠ 0 → 0 ≥ dot_product (vector_conj v) (M.mul_vec v)

-- A positive definite matrix only has positive eigenvalues
theorem pos_def_pos_eigenvalues (M : matrix n n ℂ) (e : ℂ) (h : M.is_pos_def) (ev : has_eigenvalue M e) : e > 0 :=
begin
  rw is_pos_def at h,

  have ev_prop := ev,

  rw has_eigenvalue at ev,
  cases ev with v ep,
  rw has_eigenpair at ep,
  cases h with herm mul,

  have herm_prop := herm,

  rw is_hermitian at herm,
  cases ep with vnz vmul,
  specialize mul v,
  simp only [vnz, ne.def, not_false_iff, forall_true_left] at mul,
  rw vmul at mul,
  rw dot_product_smul at mul,
  rw ← vector_sq_mag at mul,
  rw smul_eq_mul at mul,

  have vmgz : vector_sq_mag v > 0,
  {
    rw gt_iff_lt,
    rw lt_iff_le_and_ne,
    split,
    rw ← ge_iff_le,
    simp only [vector_sq_mag_geq_zero],
    rw ne.def,
    rw eq_comm,
    rw vector_zero_iff_sq_mag_zero,
    rw ← ne.def,
    exact vnz
  },

  rw vector_sq_mag_real at mul,
  rw vector_sq_mag_real at vmgz,

  have e_re : e = e.re,

  ext,
  rw complex.of_real_re,
  rw complex.of_real_im,
  apply hermitian_real_eigenvalues M herm_prop e,
  exact ev_prop,
  rw e_re at mul,
  rw e_re,

  have key : 0 < (e.re) * ((vector_sq_mag v).re) / ((vector_sq_mag v).re),
  {
    apply div_pos,
    cases mul with mul_re mul_im,
    simp only [complex.zero_re, complex.mul_re, complex.of_real_re, complex.of_real_im, mul_zero, sub_zero] at mul_re,
    exact mul_re,
    rw ← gt_iff_lt,
    simp only [gt_iff_lt, complex.zero_lt_real] at vmgz,
    rwa gt_iff_lt
  },

  rw mul_div_cancel at key,
  simp only [key, gt_iff_lt, complex.zero_lt_real],
  rw ne.def,
  by_contra vz,
  rw vz at vmgz,
  simp only [complex.of_real_zero, gt_iff_lt, lt_self_iff_false] at vmgz,
  exact vmgz,
end

-- A positive semidefinite matrix only has nonnegative eigenvalues
theorem pos_semidef_nonneg_eigenvalues (M : matrix n n ℂ) (e : ℂ) (h : M.is_pos_semidef) (ev : has_eigenvalue M e) : e ≥ 0 :=
begin
  rw is_pos_semidef at h,

  have ev_prop := ev,

  rw has_eigenvalue at ev,
  cases ev with v ep,
  rw has_eigenpair at ep,
  cases h with herm mul,

  have herm_prop := herm,

  rw is_hermitian at herm,
  cases ep with vnz vmul,
  specialize mul v,
  simp only [vnz, ne.def, not_false_iff, forall_true_left] at mul,
  rw vmul at mul,
  rw dot_product_smul at mul,
  rw ← vector_sq_mag at mul,
  rw smul_eq_mul at mul,

  have vmgz : vector_sq_mag v > 0,
  {
    rw gt_iff_lt,
    rw lt_iff_le_and_ne,
    split,
    rw ← ge_iff_le,
    simp only [vector_sq_mag_geq_zero],
    rw ne.def,
    rw eq_comm,
    rw vector_zero_iff_sq_mag_zero,
    rw ← ne.def,
    exact vnz
  },

  rw vector_sq_mag_real at mul,
  rw vector_sq_mag_real at vmgz,

  have e_re : e = e.re,
  {
    ext,
    rw complex.of_real_re,
    rw complex.of_real_im,
    apply hermitian_real_eigenvalues M herm_prop e,
    exact ev_prop
  },

  rw e_re at mul,
  rw e_re,

  have key : 0 ≤ (e.re) * ((vector_sq_mag v).re) / ((vector_sq_mag v).re),
  {
    apply div_nonneg,
    cases mul with mul_re mul_im,
    simp only [complex.zero_re, complex.mul_re, complex.of_real_re, complex.of_real_im, mul_zero, sub_zero] at mul_re,
    exact mul_re,
    rw ← ge_iff_le,
    simp only [gt_iff_lt, complex.zero_lt_real] at vmgz,
    rw ge_iff_le,
    rw lt_iff_le_and_ne at vmgz,
    cases vmgz with vge vne,
    exact vge
  },

  rw mul_div_cancel at key,
  simp only [key, ge_iff_le, complex.zero_le_real],
  rw ne.def,
  by_contra vz,
  rw vz at vmgz,
  simp only [complex.of_real_zero, gt_iff_lt, lt_self_iff_false] at vmgz,
  exact vmgz,
end

-- M is negative definite iff -M is positive definite
theorem neg_def_iff_neg_pos_def (M : matrix n n ℂ) : is_neg_def M ↔ is_pos_def (-M) :=
begin

  have important : ∀ v : n → ℂ ,  dot_product (vector_conj v) ((-M).mul_vec v) = -dot_product (vector_conj v) (M.mul_vec v),
  {
    intro v,
    rw dot_product,
    rw dot_product,
    rw ← finset.sum_neg_distrib,
    rw finset.sum_congr,
    refl,
    intros _ _,
    rw neg_mul_vec,
    rw pi.neg_apply,
    rw mul_neg
  },

  rw is_neg_def,
  rw is_pos_def,
  rw ← herm_iff_neg_herm,
  rw and.congr_right_iff,
  intro herm,

  split;

  intro prop;
  intro v;
  intro vnz;
  specialize prop v;
  simp only [vnz, ne.def, not_false_iff, forall_true_left, gt_iff_lt] at prop;
  specialize important v,
  rw important,
  simp only [prop, right.neg_pos_iff],
  rw important at prop,
  rw right.neg_pos_iff at prop,
  rw gt_iff_lt,
  exact prop,
end

-- M is negative semidefinite iff -M is positive semidefinite
theorem neg_semidef_iff_neg_pos_semidef (M : matrix n n ℂ) : is_neg_semidef M ↔ is_pos_semidef (-M) :=
begin

  have important : ∀ v : n → ℂ ,  dot_product (vector_conj v) ((-M).mul_vec v) = -dot_product (vector_conj v) (M.mul_vec v),
  {
    intro v,
    rw dot_product,
    rw dot_product,
    rw ← finset.sum_neg_distrib,
    rw finset.sum_congr,
    refl,
    intros _ _,
    rw neg_mul_vec,
    rw pi.neg_apply,
    rw mul_neg
  },

  rw is_neg_semidef,
  rw is_pos_semidef,
  rw ← herm_iff_neg_herm,
  rw and.congr_right_iff,
  intro herm,

  split;

  intro prop;
  intro v;
  intro vnz;
  specialize prop v;
  simp only [vnz, ne.def, not_false_iff, forall_true_left, gt_iff_lt] at prop;
  specialize important v,
  rw important,
  simp only [right.nonneg_neg_iff],
  rwa ← ge_iff_le,
  rw important at prop,
  rw right.nonneg_neg_iff at prop,
  exact prop,
end

-- A negative definite matrix only has negative eigenvalues
theorem neg_def_neg_eigenvalues (M : matrix n n ℂ) (e : ℂ) (h : M.is_neg_def) (ev : has_eigenvalue M e) : e < 0 :=
begin
  rw neg_def_iff_neg_pos_def at h,

  have ev_prop := ev,

  rw has_eigenvalue at ev,
  cases ev with v ep,
  rw has_eigenpair at ep,
  cases ep with vnz vmul,
  rw is_pos_def at h,
  cases h with herm_prop mul,
  rw ← herm_iff_neg_herm at herm_prop,
  specialize mul v,
  simp only [vnz, ne.def, not_false_iff, forall_true_left] at mul,

  have neg_vmul : (-M).mul_vec v = -(e • v),

  rw neg_mul_vec,
  rw neg_inj,
  exact vmul,
  rw neg_vmul at mul,
  simp only [dot_product_neg, dot_product_smul, algebra.id.smul_eq_mul, right.neg_pos_iff] at mul,
  rw ← vector_sq_mag at mul,

  have vmgz : vector_sq_mag v > 0,
  {
    rw gt_iff_lt,
    rw lt_iff_le_and_ne,
    split,
    rw ← ge_iff_le,
    simp only [vector_sq_mag_geq_zero],
    rw ne.def,
    rw eq_comm,
    rw vector_zero_iff_sq_mag_zero,
    rw ← ne.def,
    exact vnz
  },

  rw vector_sq_mag_real at mul,
  rw vector_sq_mag_real at vmgz,

  have e_re : e = e.re,

  ext,
  rw complex.of_real_re,
  rw complex.of_real_im,
  apply hermitian_real_eigenvalues M herm_prop e,
  exact ev_prop,
  rw e_re at mul,
  rw e_re,

  have key : (e.re) * ((vector_sq_mag v).re) / ((vector_sq_mag v).re) < 0,
  {
    rw div_neg_iff,
    cases mul with mul_re mul_im,
    simp only [complex.mul_re, complex.of_real_re, complex.of_real_im, mul_zero, sub_zero, complex.zero_re] at mul_re,
    simp only,
    right,
    split,
    exact mul_re,
    simp only [gt_iff_lt, complex.zero_lt_real] at vmgz,
    exact vmgz
  },

  rw mul_div_cancel at key,
  rw complex.lt_def,
  split,
  simp only [key, complex.of_real_re, complex.zero_re],
  simp only [complex.of_real_im, complex.zero_im],
  rw ne_iff_lt_or_gt,
  right,
  simp only [gt_iff_lt, complex.zero_lt_real] at vmgz,
  rwa gt_iff_lt,
end

-- A negative semidefinite matrix only has nonpositive eigenvalues
theorem neg_semidef_nonpos_eigenvalues (M : matrix n n ℂ) (e : ℂ) (h : M.is_neg_semidef) (ev : has_eigenvalue M e) : e ≤ 0 :=
begin
  rw neg_semidef_iff_neg_pos_semidef at h,

  have ev_prop := ev,

  rw has_eigenvalue at ev,
  cases ev with v ep,
  rw has_eigenpair at ep,
  cases ep with vnz vmul,
  rw is_pos_semidef at h,
  cases h with herm_prop mul,
  rw ← herm_iff_neg_herm at herm_prop,
  specialize mul v,
  simp only [vnz, ne.def, not_false_iff, forall_true_left] at mul,

  have neg_vmul : (-M).mul_vec v = -(e • v),

  rw neg_mul_vec,
  rw neg_inj,
  exact vmul,
  rw neg_vmul at mul,
  simp only [dot_product_neg, dot_product_smul, algebra.id.smul_eq_mul, right.neg_pos_iff] at mul,
  rw ← vector_sq_mag at mul,

  have vmgz : vector_sq_mag v > 0,
  {
    rw gt_iff_lt,
    rw lt_iff_le_and_ne,
    split,
    rw ← ge_iff_le,
    simp only [vector_sq_mag_geq_zero],
    rw ne.def,
    rw eq_comm,
    rw vector_zero_iff_sq_mag_zero,
    rw ← ne.def,
    exact vnz
  },

  rw vector_sq_mag_real at mul,
  rw vector_sq_mag_real at vmgz,

  have e_re : e = e.re,
  {
    ext,
    rw complex.of_real_re,
    rw complex.of_real_im,
    apply hermitian_real_eigenvalues M herm_prop e,
    exact ev_prop
  },

  rw e_re at mul,
  rw e_re,

  have key : (e.re) * ((vector_sq_mag v).re) / ((vector_sq_mag v).re) ≤ 0,
  {
    rw div_nonpos_iff,
    cases mul with mul_re mul_im,
    simp only [complex.mul_re, complex.of_real_re, complex.of_real_im, mul_zero, sub_zero, complex.zero_re] at mul_re,
    simp only,
    right,
    split,
    simp only [complex.neg_re, complex.mul_re, complex.of_real_re, complex.of_real_im, mul_zero, sub_zero, right.nonneg_neg_iff] at mul_re,
    exact mul_re,
    simp only [gt_iff_lt, complex.zero_lt_real] at vmgz,
    rw lt_iff_le_and_ne at vmgz,
    cases vmgz with vmge vne,
    exact vmge
  },

  rw mul_div_cancel at key,
  rw complex.le_def,
  split,
  simp only [key, complex.of_real_re, complex.zero_re],
  simp only [complex.of_real_im, complex.zero_im],
  rw ne_iff_lt_or_gt,
  right,
  simp only [gt_iff_lt, complex.zero_lt_real] at vmgz,
  rwa gt_iff_lt,
end

-- The sum of a positive definite matrix and a positive semidefinite matrix is positive definite
theorem pos_def_add_semidef_pos (M N : matrix n n ℂ) (hm : is_pos_def M) (hn : is_pos_semidef N) : is_pos_def (M + N) :=
begin
  rw is_pos_def,
  rw is_pos_def at hm,
  rw is_pos_semidef at hn,
  cases hm with herm_m eq_m,
  cases hn with herm_n eq_n,
  split,
  exact herm_sum M N herm_m herm_n,

  intros v vnz,
  specialize eq_m v vnz,
  specialize eq_n v vnz,
  rw matrix.add_mul_vec,
  rw dot_product_add,

  have trivial : ∀ (x y : ℂ), 0 < x → 0 ≤ y → 0 < x + y,
  {
    intros x y hx hy,
    rw complex.lt_def at hx,
    cases hx with hx_re hx_im,
    rw complex.le_def at hy,
    cases hy with hy_re hy_im,
    rw complex.lt_def,

    split,

    rw complex.add_re,
    rw complex.zero_re,
    rw complex.zero_re at hx_re hy_re,
    nlinarith,

    rw complex.add_im,
    rw complex.zero_im,
    rw complex.zero_im at hx_im hy_im,
    nlinarith,
  },

  exact trivial (vector_conj v ⬝ᵥ M.mul_vec v) (vector_conj v ⬝ᵥ N.mul_vec v) eq_m eq_n,
end

-- The sum of two positive semidefinite matrices is positive semidefinite
theorem pos_semidef_add_semidef_semidef (M N : matrix n n ℂ) (hm : is_pos_semidef M) (hn : is_pos_semidef N) : is_pos_semidef (M + N) :=
begin
  rw is_pos_semidef,
  rw is_pos_semidef at hm hn,
  cases hm with herm_m eq_m,
  cases hn with herm_n eq_n,
  split,
  exact herm_sum M N herm_m herm_n,

  intros v vnz,
  specialize eq_m v vnz,
  specialize eq_n v vnz,
  rw matrix.add_mul_vec,
  rw dot_product_add,

  have add := add_le_add eq_m eq_n,
  rw zero_add at add,

  exact add,
end

-- The sum of a negative definite matrix and a negative semidefinite matrix is negative definite
theorem neg_add_semineg_neg (M N : matrix n n ℂ) (hm : is_neg_def M) (hn : is_neg_semidef N) : is_neg_def (M + N) :=
begin
  rw is_neg_def,
  rw is_neg_def at hm,
  rw is_neg_semidef at hn,
  cases hm with herm_m eq_m,
  cases hn with herm_n eq_n,
  split,
  exact herm_sum M N herm_m herm_n,

  intros v vnz,
  specialize eq_m v vnz,
  specialize eq_n v vnz,
  rw matrix.add_mul_vec,
  rw dot_product_add,

  have trivial : ∀ (x y : ℂ), x < 0 → y ≤ 0 → x + y < 0,
  {
    intros x y hx hy,
    rw complex.lt_def at hx,
    cases hx with hx_re hx_im,
    rw complex.le_def at hy,
    cases hy with hy_re hy_im,
    rw complex.lt_def,

    split,

    rw complex.add_re,
    rw complex.zero_re,
    rw complex.zero_re at hx_re hy_re,
    nlinarith,

    rw complex.add_im,
    rw complex.zero_im,
    rw complex.zero_im at hx_im hy_im,
    nlinarith,
  },

  rw gt_iff_lt at eq_m,
  rw ge_iff_le at eq_n,
  rw gt_iff_lt,

  exact trivial (vector_conj v ⬝ᵥ M.mul_vec v) (vector_conj v ⬝ᵥ N.mul_vec v) eq_m eq_n,
end

-- The sum of two positive semidefinite matrices is positive semidefinite
theorem neg_semidef_add_semidef_semidef (M N : matrix n n ℂ) (hm : is_neg_semidef M) (hn : is_neg_semidef N) : is_neg_semidef (M + N) :=
begin
  rw is_neg_semidef,
  rw is_neg_semidef at hm hn,
  cases hm with herm_m eq_m,
  cases hn with herm_n eq_n,
  split,
  exact herm_sum M N herm_m herm_n,

  intros v vnz,
  specialize eq_m v vnz,
  specialize eq_n v vnz,
  rw matrix.add_mul_vec,
  rw dot_product_add,

  have add := add_le_add eq_m eq_n,
  rw zero_add at add,

  exact add,
end

end definite

end hermitian

-- Any two eigenvectors corresponding to different eigenvalues are linearly independent
lemma eigenvectors_linearly_independent (M : matrix n n ℂ) (u v : n → ℂ) (e r : ℂ)
(ep_1 : has_eigenpair M e u) (ep_2 : has_eigenpair M r v) (neq : e ≠ r) :
∀ (a b : ℂ), a • u + b • v = 0 → a = 0 ∧ b = 0 :=
begin
  intros a b lcz,
  rw has_eigenpair at ep_1 ep_2,
  cases ep_1 with unz ep_1,
  cases ep_2 with vnz ep_2,

  rw ne at neq vnz unz,

  have mlcz := congr_arg M.mul_vec lcz,

  rw mul_vec_add at mlcz,
  repeat {rw mul_vec_smul at mlcz},
  rw ep_1 at mlcz,
  rw ep_2 at mlcz,
  rw mul_vec_zero at mlcz,

  have elcz := congr_arg (λ x, e • x) lcz,

  dsimp at elcz,
  rw smul_add at elcz,
  rw smul_zero at elcz,
  rw smul_comm at elcz,
  rw ← elcz at mlcz,
  rw add_right_inj at mlcz,
  rw smul_comm at mlcz,

  have key : (r - e) • b • v = 0,
  {
    rw sub_smul,
    rw mlcz,
    rw sub_self,
  },

  rw smul_eq_zero at key,
  cases key with emrz key,
  exfalso,
  apply neq,
  rw sub_eq_zero at emrz,
  rw eq_comm,
  exact emrz,
  rw smul_eq_zero at key,
  rw or_comm at key,
  cases key with vz bz,
  exfalso,
  apply vnz,
  exact vz,
  split,
  swap,
  exact bz,
  rw bz at lcz,
  simp only [zero_smul, add_zero, smul_eq_zero] at lcz,
  cases lcz with az uz,
  exact az,
  exfalso,
  apply unz,
  exact uz,
end

-- Any linear combination (with non-zero coefficients) of two eigenvectors corresponding to different eigenvalues is not an eigenvector
theorem independent_eigenvectors_linear_combination_not_eigenvector (M : matrix n n ℂ) (u v : n → ℂ) (e r : ℂ) (neq : e ≠ r)
(ep_1 : has_eigenpair M e u) (ep_2 : has_eigenpair M r v) : ∀ (a b : ℂ), a ≠ 0 → b ≠ 0 → ¬(has_eigenvector M (a•u+b•v)) :=
begin
  have epeu := ep_1,
  have eprv := ep_2,

  intros a b anz bnz,
  rw ne at anz bnz neq,
  by_contra ev,
  rw has_eigenvector at ev,
  cases ev with t ep,
  rw has_eigenpair at ep,
  cases ep with lc mul,
  rw mul_vec_add at mul,
  rw smul_add at mul,
  repeat {rw mul_vec_smul at mul},

  rw has_eigenpair at ep_1 ep_2,
  cases ep_1 with unz mul_1,
  cases ep_2 with vnz mul_2,
  rw ne at unz vnz,

  rw [mul_1,mul_2] at mul,

  have helper_lemma : a • (e - t) • u + b • (r - t) • v = 0,
  {
    repeat {rw sub_smul},
    repeat {rw smul_sub},
    rw sub_add_sub_comm,
    rw sub_eq_zero,
    rw smul_comm a t,
    rw smul_comm b t,
    exact mul,
    apply_instance,
    apply_instance
  },

  have lin_ind := eigenvectors_linearly_independent M u v e r epeu eprv neq,

  specialize lin_ind (a • (e - t)) (b • (r - t)),

  have ent : ¬(e - t)=0,
  {
    by_contra eeqt,
    rw eeqt at helper_lemma,
    rw [zero_smul, smul_zero, zero_add] at helper_lemma,
    rw smul_eq_zero at helper_lemma,
    cases helper_lemma,
    apply bnz,
    exact helper_lemma,
    rw smul_eq_zero at helper_lemma,
    cases helper_lemma,
    rw sub_eq_zero at eeqt helper_lemma,
    apply neq,
    rw [helper_lemma,eeqt],
    apply vnz,
    exact helper_lemma
  },

  have rnt : ¬(r - t) = 0,
  {
    by_contra reqt,
    rw reqt at helper_lemma,
    rw [zero_smul, smul_zero, add_zero] at helper_lemma,
    rw smul_eq_zero at helper_lemma,
    cases helper_lemma,
    apply anz,
    exact helper_lemma,
    rw smul_eq_zero at helper_lemma,
    cases helper_lemma,
    apply ent,
    exact helper_lemma,
    apply unz,
    exact helper_lemma
  },

  have aemtnz : a • (e - t) ≠ 0,
  {
    rw smul_ne_zero,
    split,
    rwa ne,
    rwa ne
  },

  have brmtnz : b • (r - t) ≠ 0,
  {
    rw smul_ne_zero,
    split,
    rwa ne,
    rwa ne
  },

  repeat {rw smul_assoc at lin_ind},
  have lin_ind_2 := lin_ind helper_lemma,

  repeat {rw smul_assoc at lin_ind_2},

  rw ne at aemtnz brmtnz,
  apply aemtnz,
  cases lin_ind_2 with aemtz brmtz,
  exact aemtz,
end

-- If two matrices both have the eigenpair (e,v) then (e * e,v) is an eigenpair of their product
lemma eigenvalue_prod (M : matrix n n ℂ) (N : matrix n n ℂ)
(v : n → ℂ) (e : ℂ)  (hme : has_eigenpair M e v) (hne : has_eigenpair N e v):
has_eigenpair (M ⬝ N) (e * e) v :=
begin
  cases hme with ve hme,
  cases hne with ve hne,
  rw has_eigenpair,
  split,
  exact ve,
  rw ← mul_vec_mul_vec,
  rw hne,
  rw mul_vec_smul,
  rw hme,
  rw ← smul_assoc,
  rw ← smul_eq_mul,
end

-- (∀ v, u ⬝ v = 0) ↔ u = 0
lemma forall_left_dot_prod_zero_zero (u : n → ℂ) : (∀ v : (n → ℂ) , dot_product u v = 0) ↔ u = 0:=
begin
  split,
  intro dpz,
  funext,
  specialize dpz (λ i , if i = x then 1 else 0),
  rw dot_product at dpz,

  have key : finset.univ.sum (λ (i : n), u i * ite (i = x) 1 0) = finset.univ.sum (λ (i : n), ite (i = x) (u i) 0),
  {
    rw finset.sum_congr,
    refl,
    intros _ _,
    rw mul_ite,
    rw mul_one,
    rw mul_zero,
  },

  rw key at dpz,
  rw finset.sum_ite at dpz,
  simp only [finset.sum_const_zero, add_zero] at dpz,

  have eq_x : (λ (x_1 : n), x_1 = x) = eq x,
  {
    funext,
    rw eq_iff_iff,
    rw eq_comm
  },

  simp_rw eq_x at dpz,

  simp only at dpz,
  rw finset.filter_eq at dpz,
  simp only [finset.mem_univ, if_true, finset.sum_singleton] at dpz,
  rw dpz,
  rw pi.zero_apply,

  intro uz,
  intro v,
  rw uz,
  rw zero_dot_product,
end

-- (∀ v, v ⬝ u = 0) ↔ u = 0
lemma forall_right_dot_prod_zero_zero (u : n → ℂ) : (∀ v : (n → ℂ) , dot_product v u = 0) ↔ u = 0:=
begin
  simp_rw dot_product_comm,
  rw forall_left_dot_prod_zero_zero,
end

-- e is an eigenvalue of M iff det(M - eI)=0
lemma eigenvalue_zero_det (M : matrix n n ℂ) (e : ℂ ) :
has_eigenvalue M e  ↔ ( (M - e • (1 : matrix n n ℂ)).det = 0) :=
begin

  have eigenvector_mul_eq_zero : ∀ (v : n → ℂ), M.mul_vec v = e • v ↔  (M - e • 1).mul_vec v = 0,
  {
    intro v,
    rw sub_mul_vec,
    split,
    intro mul,
    rw mul,
    rw smul_mul_vec_assoc,
    nth_rewrite 0 ← one_mul_vec v,
    rw sub_eq_zero,

    intro sub_zero,
    rw sub_eq_zero at sub_zero,
    rw sub_zero,
    rw smul_mul_vec_assoc,
    rw one_mul_vec,
  },

  rw  ← not_iff_not,
  rw ← ne.def,
  split,
  swap,
  intro dnz,
  by_contra ev,
  rw has_eigenvalue at ev,
  cases ev with v ev,
  rw has_eigenpair at ev,
  cases ev with vnz mul,
  specialize eigenvector_mul_eq_zero v,
  rw eigenvector_mul_eq_zero at mul,

  have vec_zero := eq_zero_of_mul_vec_eq_zero dnz mul,

  apply vnz,
  exact vec_zero,

  intro n_ev,
  rw has_eigenvalue at n_ev,

  rw ne,
  by_contra,

  have det_t : ((M - e • 1)ᵀ).det = 0,
  {
    rw det_transpose,
    exact h,
  },

  have nn : ¬¬((M - e • 1).det = 0) ↔ (M - e • 1).det = 0,
  {rw not_not},

  rw ← nn at h,

  apply h,
  rw ← ne,

  rw ← nondegenerate_iff_det_ne_zero,
  rw nondegenerate,

  intro u,
  intro for_all,

  by_contra unz,

  simp_rw dot_product_mul_vec at for_all,
  rw forall_left_dot_prod_zero_zero at for_all,
  rename for_all vmu,

  have dpz := congr_arg (dot_product (vector_conj u)) vmu,

  rw dot_product_zero at dpz,

  have nn_t : ¬¬((M - e • 1)ᵀ.det = 0) ↔ (M - e • 1)ᵀ.det = 0,
  {rw not_not},

  rw ← nn_t at det_t,
  apply det_t,
  rw ← ne,

  rw ← nondegenerate_iff_det_ne_zero,
  rw nondegenerate,

  intro v,
  intro for_all_t,
  simp_rw dot_product_mul_vec at for_all_t,
  rw forall_left_dot_prod_zero_zero at for_all_t,
  rw vec_mul_trans at for_all_t,
  rw ← eigenvector_mul_eq_zero at for_all_t,
  rw not_exists at n_ev,
  specialize n_ev v,
  by_contra,
  apply n_ev,
  rw has_eigenpair,
  split,
  rwa ne,
  exact for_all_t,
end

-- The (right) eigenpairs of a matrix are the left eigenpairs of the transpose
theorem eigenpair_left_trans (M : matrix n n ℂ) (v : n → ℂ) (e : ℂ):
has_eigenpair M e v ↔ has_left_eigenpair Mᵀ e v:=
begin
  rw has_eigenpair,
  rw has_left_eigenpair,
  rw vec_mul_trans,
end

-- The eigenvalues and left eigenvalues of a matrix coincide
theorem left_eigenvalue_right_eigenvalue (M : matrix n n ℂ) (e : ℂ):
has_eigenvalue M e ↔ has_left_eigenvalue M e :=
begin

  have left_eigenvalue_trans : has_left_eigenvalue M e ↔ has_eigenvalue Mᵀ e,
  {
    rw has_eigenvalue,
    rw has_left_eigenvalue,

    split;
    intro x;
    cases x with v ep;
    use v,
    rw ← transpose_transpose M at ep,
    rw ← eigenpair_left_trans at ep,
    exact ep,
    rw eigenpair_left_trans at ep,
    rw transpose_transpose at ep,
    exact ep,
  },

  nth_rewrite 0 left_eigenvalue_trans,
  repeat {rw eigenvalue_zero_det},

  have tr : (Mᵀ - e • 1) = (M - e • 1)ᵀ,
  {simp only [transpose_sub, transpose_smul, transpose_one]},

  rw tr,
  rw det_transpose,
end

end matrix


import topology.category.Top.opens

section

open category_theory Top topological_space

lemma opens.finset_infi {ι : Type*} {X : Top} (f : ι -> opens X) (s : finset ι) (x : X) :
  (x ∈ ⨅ (i : ι) (hi : i ∈ s), f i) ↔ ∀ (i : ι), i ∈ s → x ∈ f i :=
begin
  haveI : decidable_eq ι := classical.dec_eq _,
  induction s using finset.induction_on with i s hi ih,
  { split,
    { intros hx,
      intros i hi,
      exfalso,
      exact finset.not_mem_empty _ hi, },
    { intros hx,
      simp only [infi_false, infi_top]}, },
  { split,
    { intros hx,
      intros j hj,
      rw finset.mem_insert at hj,
      rcases hj with rfl|hj,
      { simp only [finset.infi_insert] at hx,
        rw ← opens.inter_eq at hx,
        rcases hx with ⟨h1, h2⟩,
        exact h1 },
      { refine ih.mp _ j hj,
        rw finset.infi_insert at hx,
        rw ← opens.inter_eq at hx,
        rcases hx with ⟨h1, h2⟩,
        exact h2, }, },
    { intros hx,
      rw finset.infi_insert,
      rw ← opens.inter_eq,
      refine ⟨_, _⟩,
      { apply hx,
        exact finset.mem_insert_self i s, },
      { refine ih.mpr _,
        intros j hj,
        apply hx,
        exact finset.mem_insert_of_mem hj, }, }, },
end

lemma opens.fintype_infi_aux {ι : Type*} [h : fintype ι] {X : Top} (f : ι -> opens X) :
  infi f = ⨅ (i ∈ h.elems), f i :=
begin
  have h2 := h.2,
  refine le_antisymm _ _,
  { rw infi_le_iff,
    intros u h3,
    simp only [le_infi₂_iff],
    intros i hi,
    apply h3, },
  { rw infi_le_iff,
    intros i hi,
    simp only [le_infi_iff],
    intros j,
    specialize hi j,
    rw le_infi_iff at hi,
    apply hi,
    apply h.2 },
end

lemma opens.fintype_infi {ι : Type*} [fintype ι] {X : Top} (f : ι -> opens X) (x : X) :
  x ∈ infi f ↔ ∀ i : ι, x ∈ f i :=
begin
  rw opens.fintype_infi_aux,
  rw opens.finset_infi,
  split,
  { intros hi i,
    apply hi,
    exact fintype.complete i, },
  { intros hi i _,
    apply hi, },
end

end