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

! This file was ported from Lean 3 source module order.hom.set
! 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.Order.Hom.Basic
import Mathbin.Logic.Equiv.Set
import Mathbin.Data.Set.Image

/-!
# Order homomorphisms and sets

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

open OrderDual

variable {F α β γ δ : Type _}

namespace OrderIso

section LE

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

/- warning: order_iso.range_eq -> OrderIso.range_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2), Eq.{succ u2} (Set.{u2} β) (Set.range.{u2, succ u1} β α (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e)) (Set.univ.{u2} β)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2), Eq.{succ u1} (Set.{u1} β) (Set.range.{u1, succ u2} β α (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e)))) (Set.univ.{u1} β)
Case conversion may be inaccurate. Consider using '#align order_iso.range_eq OrderIso.range_eqₓ'. -/
theorem range_eq (e : α ≃o β) : Set.range e = Set.univ :=
  e.Surjective.range_eq
#align order_iso.range_eq OrderIso.range_eq

/- warning: order_iso.symm_image_image -> OrderIso.symm_image_image is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.image.{u2, u1} β α (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α _inst_2 _inst_1) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) (OrderIso.symm.{u1, u2} α β _inst_1 _inst_2 e)) (Set.image.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) s)) s
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u2} α), Eq.{succ u2} (Set.{u2} α) (Set.image.{u1, u2} β α (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u2} β α)) (RelEmbedding.toEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u2, u1} α β _inst_1 _inst_2 e)))) (Set.image.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) s)) s
Case conversion may be inaccurate. Consider using '#align order_iso.symm_image_image OrderIso.symm_image_imageₓ'. -/
@[simp]
theorem symm_image_image (e : α ≃o β) (s : Set α) : e.symm '' (e '' s) = s :=
  e.toEquiv.symm_image_image s
#align order_iso.symm_image_image OrderIso.symm_image_image

/- warning: order_iso.image_symm_image -> OrderIso.image_symm_image is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u2} β), Eq.{succ u2} (Set.{u2} β) (Set.image.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) (Set.image.{u2, u1} β α (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α _inst_2 _inst_1) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) (OrderIso.symm.{u1, u2} α β _inst_1 _inst_2 e)) s)) s
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u1} β), Eq.{succ u1} (Set.{u1} β) (Set.image.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) (Set.image.{u1, u2} β α (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u2} β α)) (RelEmbedding.toEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u2, u1} α β _inst_1 _inst_2 e)))) s)) s
Case conversion may be inaccurate. Consider using '#align order_iso.image_symm_image OrderIso.image_symm_imageₓ'. -/
@[simp]
theorem image_symm_image (e : α ≃o β) (s : Set β) : e '' (e.symm '' s) = s :=
  e.toEquiv.image_symm_image s
#align order_iso.image_symm_image OrderIso.image_symm_image

/- warning: order_iso.image_eq_preimage -> OrderIso.image_eq_preimage is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u1} α), Eq.{succ u2} (Set.{u2} β) (Set.image.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) s) (Set.preimage.{u2, u1} β α (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α _inst_2 _inst_1) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) (OrderIso.symm.{u1, u2} α β _inst_1 _inst_2 e)) s)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u2} α), Eq.{succ u1} (Set.{u1} β) (Set.image.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) s) (Set.preimage.{u1, u2} β α (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u2} β α)) (RelEmbedding.toEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u2, u1} α β _inst_1 _inst_2 e)))) s)
Case conversion may be inaccurate. Consider using '#align order_iso.image_eq_preimage OrderIso.image_eq_preimageₓ'. -/
theorem image_eq_preimage (e : α ≃o β) (s : Set α) : e '' s = e.symm ⁻¹' s :=
  e.toEquiv.image_eq_preimage s
#align order_iso.image_eq_preimage OrderIso.image_eq_preimage

/- warning: order_iso.preimage_symm_preimage -> OrderIso.preimage_symm_preimage is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.preimage.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) (Set.preimage.{u2, u1} β α (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α _inst_2 _inst_1) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) (OrderIso.symm.{u1, u2} α β _inst_1 _inst_2 e)) s)) s
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u2} α), Eq.{succ u2} (Set.{u2} α) (Set.preimage.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) (Set.preimage.{u1, u2} β α (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u2} β α)) (RelEmbedding.toEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u2, u1} α β _inst_1 _inst_2 e)))) s)) s
Case conversion may be inaccurate. Consider using '#align order_iso.preimage_symm_preimage OrderIso.preimage_symm_preimageₓ'. -/
@[simp]
theorem preimage_symm_preimage (e : α ≃o β) (s : Set α) : e ⁻¹' (e.symm ⁻¹' s) = s :=
  e.toEquiv.preimage_symm_preimage s
#align order_iso.preimage_symm_preimage OrderIso.preimage_symm_preimage

/- warning: order_iso.symm_preimage_preimage -> OrderIso.symm_preimage_preimage is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u2} β), Eq.{succ u2} (Set.{u2} β) (Set.preimage.{u2, u1} β α (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α _inst_2 _inst_1) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β _inst_2) (LE.le.{u1} α _inst_1)) (OrderIso.symm.{u1, u2} α β _inst_1 _inst_2 e)) (Set.preimage.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) s)) s
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u1} β), Eq.{succ u1} (Set.{u1} β) (Set.preimage.{u1, u2} β α (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u2} β α)) (RelEmbedding.toEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u2, u1} α β _inst_1 _inst_2 e)))) (Set.preimage.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) s)) s
Case conversion may be inaccurate. Consider using '#align order_iso.symm_preimage_preimage OrderIso.symm_preimage_preimageₓ'. -/
@[simp]
theorem symm_preimage_preimage (e : α ≃o β) (s : Set β) : e.symm ⁻¹' (e ⁻¹' s) = s :=
  e.toEquiv.symm_preimage_preimage s
#align order_iso.symm_preimage_preimage OrderIso.symm_preimage_preimage

/- warning: order_iso.image_preimage -> OrderIso.image_preimage is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u2} β), Eq.{succ u2} (Set.{u2} β) (Set.image.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) (Set.preimage.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) s)) s
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u1} β), Eq.{succ u1} (Set.{u1} β) (Set.image.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) (Set.preimage.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) s)) s
Case conversion may be inaccurate. Consider using '#align order_iso.image_preimage OrderIso.image_preimageₓ'. -/
@[simp]
theorem image_preimage (e : α ≃o β) (s : Set β) : e '' (e ⁻¹' s) = s :=
  e.toEquiv.image_preimage s
#align order_iso.image_preimage OrderIso.image_preimage

/- warning: order_iso.preimage_image -> OrderIso.preimage_image is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LE.{u1} α] [_inst_2 : LE.{u2} β] (e : OrderIso.{u1, u2} α β _inst_1 _inst_2) (s : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.preimage.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) (Set.image.{u1, u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β _inst_1 _inst_2) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α _inst_1) (LE.le.{u2} β _inst_2)) e) s)) s
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LE.{u2} α] [_inst_2 : LE.{u1} β] (e : OrderIso.{u2, u1} α β _inst_1 _inst_2) (s : Set.{u2} α), Eq.{succ u2} (Set.{u2} α) (Set.preimage.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) (Set.image.{u2, u1} α β (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α _inst_1 x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β _inst_2 x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) e))) s)) s
Case conversion may be inaccurate. Consider using '#align order_iso.preimage_image OrderIso.preimage_imageₓ'. -/
@[simp]
theorem preimage_image (e : α ≃o β) (s : Set α) : e ⁻¹' (e '' s) = s :=
  e.toEquiv.preimage_image s
#align order_iso.preimage_image OrderIso.preimage_image

end LE

open Set

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

#print OrderIso.setCongr /-
/-- Order isomorphism between two equal sets. -/
def setCongr (s t : Set α) (h : s = t) : s ≃o t
    where
  toEquiv := Equiv.setCongr h
  map_rel_iff' x y := Iff.rfl
#align order_iso.set_congr OrderIso.setCongr
-/

#print OrderIso.Set.univ /-
/-- Order isomorphism between `univ : set α` and `α`. -/
def Set.univ : (Set.univ : Set α) ≃o α
    where
  toEquiv := Equiv.Set.univ α
  map_rel_iff' x y := Iff.rfl
#align order_iso.set.univ OrderIso.Set.univ
-/

end OrderIso

#print StrictMonoOn.orderIso /-
/-- If a function `f` is strictly monotone on a set `s`, then it defines an order isomorphism
between `s` and its image. -/
protected noncomputable def StrictMonoOn.orderIso {α β} [LinearOrder α] [Preorder β] (f : α → β)
    (s : Set α) (hf : StrictMonoOn f s) : s ≃o f '' s
    where
  toEquiv := hf.InjOn.bijOn_image.Equiv _
  map_rel_iff' x y := hf.le_iff_le x.2 y.2
#align strict_mono_on.order_iso StrictMonoOn.orderIso
-/

namespace StrictMono

variable {α β} [LinearOrder α] [Preorder β]

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

#print StrictMono.orderIso /-
/-- A strictly monotone function from a linear order is an order isomorphism between its domain and
its range. -/
@[simps apply]
protected noncomputable def orderIso : α ≃o Set.range f
    where
  toEquiv := Equiv.ofInjective f h_mono.Injective
  map_rel_iff' a b := h_mono.le_iff_le
#align strict_mono.order_iso StrictMono.orderIso
-/

#print StrictMono.orderIsoOfSurjective /-
/-- A strictly monotone surjective function from a linear order is an order isomorphism. -/
noncomputable def orderIsoOfSurjective : α ≃o β :=
  (h_mono.OrderIso f).trans <| (OrderIso.setCongr _ _ h_surj.range_eq).trans OrderIso.Set.univ
#align strict_mono.order_iso_of_surjective StrictMono.orderIsoOfSurjective
-/

/- warning: strict_mono.coe_order_iso_of_surjective -> StrictMono.coe_orderIsoOfSurjective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} β] (f : α -> β) (h_mono : StrictMono.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) _inst_2 f) (h_surj : Function.Surjective.{succ u1, succ u2} α β f), Eq.{max (succ u1) (succ u2)} ((fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))) (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2))) => α -> β) (StrictMono.orderIsoOfSurjective.{u1, u2} α β _inst_1 _inst_2 f h_mono h_surj)) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (OrderIso.{u1, u2} α β (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (Preorder.toLE.{u2} β _inst_2)) (fun (_x : RelIso.{u1, u2} α β (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))) (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2))) => α -> β) (RelIso.hasCoeToFun.{u1, u2} α β (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))) (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2))) (StrictMono.orderIsoOfSurjective.{u1, u2} α β _inst_1 _inst_2 f h_mono h_surj)) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : Preorder.{u1} β] (f : α -> β) (h_mono : StrictMono.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) _inst_2 f) (h_surj : Function.Surjective.{succ u2, succ u1} α β f), Eq.{max (succ u2) (succ u1)} (forall (a : α), (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α (fun (_x : α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : α) => β) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} α β) α β (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} α β)) (RelEmbedding.toEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β (Preorder.toLE.{u1} β _inst_2) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} α β (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : α) => LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : β) => LE.le.{u1} β (Preorder.toLE.{u1} β _inst_2) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (StrictMono.orderIsoOfSurjective.{u2, u1} α β _inst_1 _inst_2 f h_mono h_surj)))) f
Case conversion may be inaccurate. Consider using '#align strict_mono.coe_order_iso_of_surjective StrictMono.coe_orderIsoOfSurjectiveₓ'. -/
@[simp]
theorem coe_orderIsoOfSurjective : (orderIsoOfSurjective f h_mono h_surj : α → β) = f :=
  rfl
#align strict_mono.coe_order_iso_of_surjective StrictMono.coe_orderIsoOfSurjective

/- warning: strict_mono.order_iso_of_surjective_symm_apply_self -> StrictMono.orderIsoOfSurjective_symm_apply_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} β] (f : α -> β) (h_mono : StrictMono.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) _inst_2 f) (h_surj : Function.Surjective.{succ u1, succ u2} α β f) (a : α), Eq.{succ u1} α (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α (Preorder.toLE.{u2} β _inst_2) (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))))) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))))) (OrderIso.symm.{u1, u2} α β (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (Preorder.toLE.{u2} β _inst_2) (StrictMono.orderIsoOfSurjective.{u1, u2} α β _inst_1 _inst_2 f h_mono h_surj)) (f a)) a
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : Preorder.{u1} β] (f : α -> β) (h_mono : StrictMono.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) _inst_2 f) (h_surj : Function.Surjective.{succ u2, succ u1} α β f) (a : α), Eq.{succ u2} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) (f a)) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u1) (succ u2), succ u1, succ u2} (Function.Embedding.{succ u1, succ u2} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u2} β α)) (RelEmbedding.toEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β (Preorder.toLE.{u1} β _inst_2) x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u1, u2} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u1} β (Preorder.toLE.{u1} β _inst_2) x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u2, u1} α β (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) (Preorder.toLE.{u1} β _inst_2) (StrictMono.orderIsoOfSurjective.{u2, u1} α β _inst_1 _inst_2 f h_mono h_surj)))) (f a)) a
Case conversion may be inaccurate. Consider using '#align strict_mono.order_iso_of_surjective_symm_apply_self StrictMono.orderIsoOfSurjective_symm_apply_selfₓ'. -/
@[simp]
theorem orderIsoOfSurjective_symm_apply_self (a : α) :
    (orderIsoOfSurjective f h_mono h_surj).symm (f a) = a :=
  (orderIsoOfSurjective f h_mono h_surj).symm_apply_apply _
#align strict_mono.order_iso_of_surjective_symm_apply_self StrictMono.orderIsoOfSurjective_symm_apply_self

/- warning: strict_mono.order_iso_of_surjective_self_symm_apply -> StrictMono.orderIsoOfSurjective_self_symm_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} β] (f : α -> β) (h_mono : StrictMono.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) _inst_2 f) (h_surj : Function.Surjective.{succ u1, succ u2} α β f) (b : β), Eq.{succ u2} β (f (coeFn.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (OrderIso.{u2, u1} β α (Preorder.toLE.{u2} β _inst_2) (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))) (fun (_x : RelIso.{u2, u1} β α (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))))) => β -> α) (RelIso.hasCoeToFun.{u2, u1} β α (LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))))) (OrderIso.symm.{u1, u2} α β (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (Preorder.toLE.{u2} β _inst_2) (StrictMono.orderIsoOfSurjective.{u1, u2} α β _inst_1 _inst_2 f h_mono h_surj)) b)) b
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} β] (f : α -> β) (h_mono : StrictMono.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) _inst_2 f) (h_surj : Function.Surjective.{succ u1, succ u2} α β f) (b : β), Eq.{succ u2} β (f (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} β α) β (fun (_x : β) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : β) => α) _x) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Function.Embedding.{succ u2, succ u1} β α) β α (Function.instEmbeddingLikeEmbedding.{succ u2, succ u1} β α)) (RelEmbedding.toEmbedding.{u2, u1} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2) x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (RelIso.toRelEmbedding.{u2, u1} β α (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1281 : β) (x._@.Mathlib.Order.Hom.Basic._hyg.1283 : β) => LE.le.{u2} β (Preorder.toLE.{u2} β _inst_2) x._@.Mathlib.Order.Hom.Basic._hyg.1281 x._@.Mathlib.Order.Hom.Basic._hyg.1283) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.1296 : α) (x._@.Mathlib.Order.Hom.Basic._hyg.1298 : α) => LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) x._@.Mathlib.Order.Hom.Basic._hyg.1296 x._@.Mathlib.Order.Hom.Basic._hyg.1298) (OrderIso.symm.{u1, u2} α β (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) (Preorder.toLE.{u2} β _inst_2) (StrictMono.orderIsoOfSurjective.{u1, u2} α β _inst_1 _inst_2 f h_mono h_surj)))) b)) b
Case conversion may be inaccurate. Consider using '#align strict_mono.order_iso_of_surjective_self_symm_apply StrictMono.orderIsoOfSurjective_self_symm_applyₓ'. -/
theorem orderIsoOfSurjective_self_symm_apply (b : β) :
    f ((orderIsoOfSurjective f h_mono h_surj).symm b) = b :=
  (orderIsoOfSurjective f h_mono h_surj).apply_symm_apply _
#align strict_mono.order_iso_of_surjective_self_symm_apply StrictMono.orderIsoOfSurjective_self_symm_apply

end StrictMono

section BooleanAlgebra

variable (α) [BooleanAlgebra α]

#print OrderIso.compl /-
/-- Taking complements as an order isomorphism to the order dual. -/
@[simps]
def OrderIso.compl : α ≃o αᵒᵈ where
  toFun := OrderDual.toDual ∘ compl
  invFun := compl ∘ OrderDual.ofDual
  left_inv := compl_compl
  right_inv := compl_compl
  map_rel_iff' x y := compl_le_compl_iff_le
#align order_iso.compl OrderIso.compl
-/

#print compl_strictAnti /-
theorem compl_strictAnti : StrictAnti (compl : α → α) :=
  (OrderIso.compl α).StrictMono
#align compl_strict_anti compl_strictAnti
-/

#print compl_antitone /-
theorem compl_antitone : Antitone (compl : α → α) :=
  (OrderIso.compl α).Monotone
#align compl_antitone compl_antitone
-/

end BooleanAlgebra


/-
Copyright (c) 2022 Jujian Zhang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jujian Zhang, Scott Morrison
-/
import category_theory.preadditive.injective_resolution
import category_theory.abelian.exact
import algebra.homology.homotopy_category

/-!
# Main result

When the underlying category is abelian:
* `category_theory.InjectiveResolution.desc`: Given `I : InjectiveResolution X` and
  `J : InjectiveResolution Y`, any morphism `X ⟶ Y` admits a descent to a chain map
  `J.cocomplex ⟶ I.cocomplex`. It is a descent in the sense that `I.ι` intertwines the descent and
  the original morphism, see `category_theory.InjectiveResolution.desc_commutes`.
* `category_theory.InjectiveResolution.desc_homotopy`: Any two such descents are homotopic.
* `category_theory.InjectiveResolution.homotopy_equiv`: Any two injective resolutions of the same
  object are homotopy equivalent.
* `category_theory.injective_resolutions`: If every object admits an injective resolution, we can
  construct a functor `injective_resolutions C : C ⥤ homotopy_category C`.

* `category_theory.exact_f_d`: `f` and `injective.d f` are exact.
* `category_theory.InjectiveResolution.of`: Hence, starting from a monomorphism `X ⟶ J`, where `J`
  is injective, we can apply `injective.d` repeatedly to obtain an injective resolution of `X`.
-/

noncomputable theory

open category_theory
open category_theory.limits

universes v u

namespace category_theory
variables {C : Type u} [category.{v} C]

open injective

namespace InjectiveResolution
section
variables [has_zero_morphisms C] [has_zero_object C] [has_equalizers C] [has_images C]
/-- Auxiliary construction for `desc`. -/
def desc_f_zero {Y Z : C} (f : Z ⟶ Y) (I : InjectiveResolution Y) (J : InjectiveResolution Z) :
  J.cocomplex.X 0 ⟶ I.cocomplex.X 0 :=
factor_thru (f ≫ I.ι.f 0) (J.ι.f 0)

end

section abelian
variables [abelian C]
/-- Auxiliary construction for `desc`. -/
def desc_f_one {Y Z : C}
  (f : Z ⟶ Y) (I : InjectiveResolution Y) (J : InjectiveResolution Z) :
  J.cocomplex.X 1 ⟶ I.cocomplex.X 1 :=
exact.desc (desc_f_zero f I J ≫ I.cocomplex.d 0 1) (J.ι.f 0) (J.cocomplex.d 0 1)
  (abelian.exact.op _ _ J.exact₀) (by simp [←category.assoc, desc_f_zero])

@[simp] lemma desc_f_one_zero_comm {Y Z : C}
  (f : Z ⟶ Y) (I : InjectiveResolution Y) (J : InjectiveResolution Z) :
  J.cocomplex.d 0 1 ≫ desc_f_one f I J = desc_f_zero f I J ≫ I.cocomplex.d 0 1 :=
by simp [desc_f_zero, desc_f_one]

/-- Auxiliary construction for `desc`. -/
def desc_f_succ {Y Z : C}
  (I : InjectiveResolution Y) (J : InjectiveResolution Z)
  (n : ℕ) (g : J.cocomplex.X n ⟶ I.cocomplex.X n) (g' : J.cocomplex.X (n+1) ⟶ I.cocomplex.X (n+1))
  (w : J.cocomplex.d n (n+1) ≫ g' = g ≫ I.cocomplex.d n (n+1)) :
  Σ' g'' : J.cocomplex.X (n+2) ⟶ I.cocomplex.X (n+2),
    J.cocomplex.d (n+1) (n+2) ≫ g'' = g' ≫ I.cocomplex.d (n+1) (n+2) :=
⟨@exact.desc C _ _ _ _ _ _ _ _ _
  (g' ≫ I.cocomplex.d (n+1) (n+2))
  (J.cocomplex.d n (n+1))
  (J.cocomplex.d (n+1) (n+2)) (abelian.exact.op _ _ (J.exact _))
  (by simp [←category.assoc, w]), (by simp)⟩

/-- A morphism in `C` descends to a chain map between injective resolutions. -/
def desc {Y Z : C}
  (f : Z ⟶ Y) (I : InjectiveResolution Y) (J : InjectiveResolution Z) :
  J.cocomplex ⟶ I.cocomplex :=
cochain_complex.mk_hom _ _ (desc_f_zero f _ _) (desc_f_one f _ _)
  (desc_f_one_zero_comm f I J).symm
  (λ n ⟨g, g', w⟩, ⟨(desc_f_succ I J n g g' w.symm).1, (desc_f_succ I J n g g' w.symm).2.symm⟩)

/-- The resolution maps intertwine the descent of a morphism and that morphism. -/
@[simp, reassoc]
lemma desc_commutes {Y Z : C}
  (f : Z ⟶ Y) (I : InjectiveResolution Y) (J : InjectiveResolution Z) :
  J.ι ≫ desc f I J = (cochain_complex.single₀ C).map f ≫ I.ι :=
begin
  ext n,
  rcases n with (_|_|n);
  { dsimp [desc, desc_f_one, desc_f_zero], simp, },
end

-- Now that we've checked this property of the descent,
-- we can seal away the actual definition.
attribute [irreducible] desc

/-- An auxiliary definition for `desc_homotopy_zero`. -/
def desc_homotopy_zero_zero {Y Z : C} {I : InjectiveResolution Y} {J : InjectiveResolution Z}
  (f : I.cocomplex ⟶ J.cocomplex)
  (comm : I.ι ≫ f = 0) : I.cocomplex.X 1 ⟶ J.cocomplex.X 0 :=
exact.desc (f.f 0) (I.ι.f 0) (I.cocomplex.d 0 1) (abelian.exact.op _ _ I.exact₀)
  (congr_fun (congr_arg homological_complex.hom.f comm) 0)

/-- An auxiliary definition for `desc_homotopy_zero`. -/
def desc_homotopy_zero_one {Y Z : C} {I : InjectiveResolution Y} {J : InjectiveResolution Z}
  (f : I.cocomplex ⟶ J.cocomplex)
  (comm : I.ι ≫ f = (0 : _ ⟶ J.cocomplex)) : I.cocomplex.X 2 ⟶ J.cocomplex.X 1 :=
exact.desc (f.f 1 - desc_homotopy_zero_zero f comm ≫ J.cocomplex.d 0 1)
  (I.cocomplex.d 0 1) (I.cocomplex.d 1 2) (abelian.exact.op _ _ (I.exact _))
  (by simp [desc_homotopy_zero_zero, ←category.assoc])

/-- An auxiliary definition for `desc_homotopy_zero`. -/
def desc_homotopy_zero_succ {Y Z : C} {I : InjectiveResolution Y} {J : InjectiveResolution Z}
  (f : I.cocomplex ⟶ J.cocomplex) (n : ℕ)
  (g : I.cocomplex.X (n + 1) ⟶ J.cocomplex.X n)
  (g' : I.cocomplex.X (n + 2) ⟶ J.cocomplex.X (n + 1))
  (w : f.f (n + 1) = I.cocomplex.d (n+1) (n+2) ≫ g' + g ≫ J.cocomplex.d n (n+1)) :
  I.cocomplex.X (n + 3) ⟶ J.cocomplex.X (n + 2) :=
exact.desc (f.f (n+2) - g' ≫ J.cocomplex.d _ _) (I.cocomplex.d (n+1) (n+2))
  (I.cocomplex.d (n+2) (n+3)) (abelian.exact.op _ _ (I.exact _))
  (by simp [preadditive.comp_sub, ←category.assoc, preadditive.sub_comp,
        show I.cocomplex.d (n+1) (n+2) ≫ g' = f.f (n + 1) - g ≫ J.cocomplex.d n (n+1),
        by {rw w, simp only [add_sub_cancel] } ])

/-- Any descent of the zero morphism is homotopic to zero. -/
def desc_homotopy_zero {Y Z : C} {I : InjectiveResolution Y} {J : InjectiveResolution Z}
  (f : I.cocomplex ⟶ J.cocomplex)
  (comm : I.ι ≫ f = 0) :
  homotopy f 0 :=
homotopy.mk_coinductive _ (desc_homotopy_zero_zero f comm) (by simp [desc_homotopy_zero_zero])
  (desc_homotopy_zero_one f comm) (by simp [desc_homotopy_zero_one])
  (λ n ⟨g, g', w⟩, ⟨desc_homotopy_zero_succ f n g g' (by simp only [w, add_comm]),
    by simp [desc_homotopy_zero_succ, w]⟩)

/-- Two descents of the same morphism are homotopic. -/
def desc_homotopy {Y Z : C} (f : Y ⟶ Z) {I : InjectiveResolution Y} {J : InjectiveResolution Z}
  (g h : I.cocomplex ⟶ J.cocomplex)
  (g_comm : I.ι ≫ g = (cochain_complex.single₀ C).map f ≫ J.ι)
  (h_comm : I.ι ≫ h = (cochain_complex.single₀ C).map f ≫ J.ι) :
  homotopy g h :=
homotopy.equiv_sub_zero.inv_fun (desc_homotopy_zero _ (by simp [g_comm, h_comm]))

/-- The descent of the identity morphism is homotopic to the identity cochain map. -/
def desc_id_homotopy (X : C) (I : InjectiveResolution X) :
  homotopy (desc (𝟙 X) I I) (𝟙 I.cocomplex) :=
by apply desc_homotopy (𝟙 X); simp

/-- The descent of a composition is homotopic to the composition of the descents. -/
def desc_comp_homotopy {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z)
  (I : InjectiveResolution X) (J : InjectiveResolution Y) (K : InjectiveResolution Z) :
  homotopy (desc (f ≫ g) K I) (desc f J I ≫ desc g K J)  :=
by apply desc_homotopy (f ≫ g); simp

-- We don't care about the actual definitions of these homotopies.
attribute [irreducible] desc_homotopy_zero desc_homotopy desc_id_homotopy desc_comp_homotopy

/-- Any two injective resolutions are homotopy equivalent. -/
def homotopy_equiv {X : C} (I J : InjectiveResolution X) :
  homotopy_equiv I.cocomplex J.cocomplex :=
{ hom := desc (𝟙 X) J I,
  inv := desc (𝟙 X) I J,
  homotopy_hom_inv_id := (desc_comp_homotopy (𝟙 X) (𝟙 X) I J I).symm.trans $
    by simpa [category.id_comp] using desc_id_homotopy _ _,
  homotopy_inv_hom_id := (desc_comp_homotopy (𝟙 X) (𝟙 X) J I J).symm.trans $
    by simpa [category.id_comp] using desc_id_homotopy _ _ }

@[simp, reassoc] lemma homotopy_equiv_hom_ι {X : C} (I J : InjectiveResolution X) :
  I.ι ≫ (homotopy_equiv I J).hom = J.ι :=
by simp [homotopy_equiv]

@[simp, reassoc] lemma homotopy_equiv_inv_ι {X : C} (I J : InjectiveResolution X) :
  J.ι ≫ (homotopy_equiv I J).inv = I.ι :=
by simp [homotopy_equiv]

end abelian

end InjectiveResolution

section
variables [abelian C]

/-- An arbitrarily chosen injective resolution of an object. -/
abbreviation injective_resolution (Z : C) [has_injective_resolution Z] : cochain_complex C ℕ :=
(has_injective_resolution.out Z).some.cocomplex

/-- The cochain map from cochain complex consisting of `Z` supported in degree `0`
back to the arbitrarily chosen injective resolution `injective_resolution Z`. -/
abbreviation injective_resolution.ι (Z : C) [has_injective_resolution Z] :
  (cochain_complex.single₀ C).obj Z ⟶ injective_resolution Z :=
(has_injective_resolution.out Z).some.ι

/-- The descent of a morphism to a cochain map between the arbitrarily chosen injective resolutions.
-/
abbreviation injective_resolution.desc {X Y : C} (f : X ⟶ Y)
  [has_injective_resolution X] [has_injective_resolution Y] :
  injective_resolution X ⟶ injective_resolution Y :=
InjectiveResolution.desc f _ _

variables (C) [has_injective_resolutions C]

/--
Taking injective resolutions is functorial,
if considered with target the homotopy category
(`ℕ`-indexed cochain complexes and chain maps up to homotopy).
-/
def injective_resolutions : C ⥤ homotopy_category C (complex_shape.up ℕ) :=
{ obj := λ X, (homotopy_category.quotient _ _).obj (injective_resolution X),
  map := λ X Y f, (homotopy_category.quotient _ _).map (injective_resolution.desc f),
  map_id' := λ X, begin
    rw ←(homotopy_category.quotient _ _).map_id,
    apply homotopy_category.eq_of_homotopy,
    apply InjectiveResolution.desc_id_homotopy,
  end,
  map_comp' := λ X Y Z f g, begin
    rw ←(homotopy_category.quotient _ _).map_comp,
    apply homotopy_category.eq_of_homotopy,
    apply InjectiveResolution.desc_comp_homotopy,
  end, }

end

section

variables [abelian C] [enough_injectives C]

lemma exact_f_d {X Y : C} (f : X ⟶ Y) : exact f (d f) :=
(abelian.exact_iff _ _).2 $
  ⟨by simp, zero_of_comp_mono (ι _) $ by rw [category.assoc, kernel.condition]⟩

end

namespace InjectiveResolution
/-!
Our goal is to define `InjectiveResolution.of Z : InjectiveResolution Z`.
The `0`-th object in this resolution will just be `injective.under Z`,
i.e. an arbitrarily chosen injective object with a map from `Z`.
After that, we build the `n+1`-st object as `injective.syzygies`
applied to the previously constructed morphism,
and the map from the `n`-th object as `injective.d`.
-/

variables [abelian C] [enough_injectives C]

/-- Auxiliary definition for `InjectiveResolution.of`. -/
@[simps]
def of_cocomplex (Z : C) : cochain_complex C ℕ :=
cochain_complex.mk'
  (injective.under Z) (injective.syzygies (injective.ι Z)) (injective.d (injective.ι Z))
  (λ ⟨X, Y, f⟩, ⟨injective.syzygies f, injective.d f, (exact_f_d f).w⟩)

/--
In any abelian category with enough injectives,
`InjectiveResolution.of Z` constructs an injective resolution of the object `Z`.
-/
@[irreducible] def of (Z : C) : InjectiveResolution Z :=
{ cocomplex := of_cocomplex Z,
  ι := cochain_complex.mk_hom _ _ (injective.ι Z) 0
    (by { simp only [of_cocomplex_d, eq_self_iff_true, eq_to_hom_refl, category.comp_id,
      dite_eq_ite, if_true, comp_zero],
      exact (exact_f_d (injective.ι Z)).w, } ) (λ n _, ⟨0, by ext⟩),
  injective := by { rintros (_|_|_|n); { apply injective.injective_under, } },
  exact₀ := by simpa using exact_f_d (injective.ι Z),
  exact := by { rintros (_|n); { simp, apply exact_f_d } },
  mono := injective.ι_mono Z }

@[priority 100]
instance (Z : C) : has_injective_resolution Z :=
{ out := ⟨of Z⟩ }

@[priority 100]
instance : has_injective_resolutions C :=
{ out := λ _, infer_instance }

end InjectiveResolution

end category_theory


/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.concrete_category.bundled_hom
import algebra.punit_instances
import category_theory.functor.reflects_isomorphisms

/-!
# Category instances for monoid, add_monoid, comm_monoid, and add_comm_monoid.

We introduce the bundled categories:
* `Mon`
* `AddMon`
* `CommMon`
* `AddCommMon`
along with the relevant forgetful functors between them.
-/

universes u v

open category_theory

/-- The category of monoids and monoid morphisms. -/
@[to_additive AddMon]
def Mon : Type (u+1) := bundled monoid

/-- The category of additive monoids and monoid morphisms. -/
add_decl_doc AddMon

namespace Mon

/-- `monoid_hom` doesn't actually assume associativity. This alias is needed to make the category
theory machinery work. -/
@[to_additive "`add_monoid_hom` doesn't actually assume associativity. This alias is needed to make
the category theory machinery work."]
abbreviation assoc_monoid_hom (M N : Type*) [monoid M] [monoid N] := monoid_hom M N

@[to_additive]
instance bundled_hom : bundled_hom assoc_monoid_hom :=
⟨λ M N [monoid M] [monoid N], by exactI @monoid_hom.to_fun M N _ _,
 λ M [monoid M], by exactI @monoid_hom.id M _,
 λ M N P [monoid M] [monoid N] [monoid P], by exactI @monoid_hom.comp M N P _ _ _,
 λ M N [monoid M] [monoid N], by exactI @monoid_hom.coe_inj M N _ _⟩

attribute [derive [large_category, concrete_category]] Mon
attribute [to_additive] Mon.large_category Mon.concrete_category

@[to_additive] instance : has_coe_to_sort Mon Type* := bundled.has_coe_to_sort

/-- Construct a bundled `Mon` from the underlying type and typeclass. -/
@[to_additive]
def of (M : Type u) [monoid M] : Mon := bundled.of M

/-- Construct a bundled `Mon` from the underlying type and typeclass. -/
add_decl_doc AddMon.of

/-- Typecheck a `monoid_hom` as a morphism in `Mon`. -/
@[to_additive] def of_hom {X Y : Type u} [monoid X] [monoid Y] (f : X →* Y) :
  of X ⟶ of Y := f

/-- Typecheck a `add_monoid_hom` as a morphism in `AddMon`. -/
add_decl_doc AddMon.of_hom

@[simp] lemma of_hom_apply {X Y : Type u} [monoid X] [monoid Y] (f : X →* Y)
  (x : X) : of_hom f x = f x := rfl

@[to_additive]
instance : inhabited Mon :=
-- The default instance for `monoid punit` is derived via `punit.comm_ring`,
-- which breaks to_additive.
⟨@of punit $ @group.to_monoid _ $ @comm_group.to_group _ punit.comm_group⟩

@[to_additive]
instance (M : Mon) : monoid M := M.str

@[simp, to_additive] lemma coe_of (R : Type u) [monoid R] : (Mon.of R : Type u) = R := rfl

end Mon

/-- The category of commutative monoids and monoid morphisms. -/
@[to_additive AddCommMon]
def CommMon : Type (u+1) := bundled comm_monoid

/-- The category of additive commutative monoids and monoid morphisms. -/
add_decl_doc AddCommMon

namespace CommMon

@[to_additive]
instance : bundled_hom.parent_projection comm_monoid.to_monoid := ⟨⟩

attribute [derive [large_category, concrete_category]] CommMon
attribute [to_additive] CommMon.large_category CommMon.concrete_category

@[to_additive] instance : has_coe_to_sort CommMon Type* := bundled.has_coe_to_sort

/-- Construct a bundled `CommMon` from the underlying type and typeclass. -/
@[to_additive]
def of (M : Type u) [comm_monoid M] : CommMon := bundled.of M

/-- Construct a bundled `AddCommMon` from the underlying type and typeclass. -/
add_decl_doc AddCommMon.of

@[to_additive]
instance : inhabited CommMon :=
-- The default instance for `comm_monoid punit` is derived via `punit.comm_ring`,
-- which breaks to_additive.
⟨@of punit $ @comm_group.to_comm_monoid _ punit.comm_group⟩

@[to_additive]
instance (M : CommMon) : comm_monoid M := M.str

@[simp, to_additive] lemma coe_of (R : Type u) [comm_monoid R] : (CommMon.of R : Type u) = R := rfl

@[to_additive has_forget_to_AddMon]
instance has_forget_to_Mon : has_forget₂ CommMon Mon := bundled_hom.forget₂ _ _

@[to_additive] instance : has_coe CommMon.{u} Mon.{u} :=
{ coe := (forget₂ CommMon Mon).obj, }

end CommMon

-- We verify that the coercions of morphisms to functions work correctly:
example {R S : Mon}     (f : R ⟶ S) : (R : Type) → (S : Type) := f
example {R S : CommMon} (f : R ⟶ S) : (R : Type) → (S : Type) := f

-- We verify that when constructing a morphism in `CommMon`,
-- when we construct the `to_fun` field, the types are presented as `↥R`,
-- rather than `R.α` or (as we used to have) `↥(bundled.map comm_monoid.to_monoid R)`.
example (R : CommMon.{u}) : R ⟶ R :=
{ to_fun := λ x,
  begin
    match_target (R : Type u),
    match_hyp x : (R : Type u),
    exact x * x
  end ,
  map_one' := by simp,
  map_mul' := λ x y,
  begin rw [mul_assoc x y (x * y), ←mul_assoc y x y, mul_comm y x, mul_assoc, mul_assoc], end, }

variables {X Y : Type u}

section
variables [monoid X] [monoid Y]

/-- Build an isomorphism in the category `Mon` from a `mul_equiv` between `monoid`s. -/
@[to_additive add_equiv.to_AddMon_iso "Build an isomorphism in the category `AddMon` from
an `add_equiv` between `add_monoid`s.", simps]
def mul_equiv.to_Mon_iso (e : X ≃* Y) : Mon.of X ≅ Mon.of Y :=
{ hom := e.to_monoid_hom,
  inv := e.symm.to_monoid_hom }

end

section
variables [comm_monoid X] [comm_monoid Y]

/-- Build an isomorphism in the category `CommMon` from a `mul_equiv` between `comm_monoid`s. -/
@[to_additive add_equiv.to_AddCommMon_iso "Build an isomorphism in the category `AddCommMon`
from an `add_equiv` between `add_comm_monoid`s.", simps]
def mul_equiv.to_CommMon_iso (e : X ≃* Y) : CommMon.of X ≅ CommMon.of Y :=
{ hom := e.to_monoid_hom,
  inv := e.symm.to_monoid_hom }

end

namespace category_theory.iso

/-- Build a `mul_equiv` from an isomorphism in the category `Mon`. -/
@[to_additive AddMon_iso_to_add_equiv "Build an `add_equiv` from an isomorphism in the category
`AddMon`."]
def Mon_iso_to_mul_equiv {X Y : Mon} (i : X ≅ Y) : X ≃* Y :=
i.hom.to_mul_equiv i.inv i.hom_inv_id i.inv_hom_id

/-- Build a `mul_equiv` from an isomorphism in the category `CommMon`. -/
@[to_additive "Build an `add_equiv` from an isomorphism in the category
`AddCommMon`."]
def CommMon_iso_to_mul_equiv {X Y : CommMon} (i : X ≅ Y) : X ≃* Y :=
i.hom.to_mul_equiv i.inv i.hom_inv_id i.inv_hom_id

end category_theory.iso

/-- multiplicative equivalences between `monoid`s are the same as (isomorphic to) isomorphisms
in `Mon` -/
@[to_additive add_equiv_iso_AddMon_iso "additive equivalences between `add_monoid`s are the same
as (isomorphic to) isomorphisms in `AddMon`"]
def mul_equiv_iso_Mon_iso {X Y : Type u} [monoid X] [monoid Y] :
  (X ≃* Y) ≅ (Mon.of X ≅ Mon.of Y) :=
{ hom := λ e, e.to_Mon_iso,
  inv := λ i, i.Mon_iso_to_mul_equiv, }

/-- multiplicative equivalences between `comm_monoid`s are the same as (isomorphic to) isomorphisms
in `CommMon` -/
@[to_additive add_equiv_iso_AddCommMon_iso "additive equivalences between `add_comm_monoid`s are
the same as (isomorphic to) isomorphisms in `AddCommMon`"]
def mul_equiv_iso_CommMon_iso {X Y : Type u} [comm_monoid X] [comm_monoid Y] :
  (X ≃* Y) ≅ (CommMon.of X ≅ CommMon.of Y) :=
{ hom := λ e, e.to_CommMon_iso,
  inv := λ i, i.CommMon_iso_to_mul_equiv, }

@[to_additive]
instance Mon.forget_reflects_isos : reflects_isomorphisms (forget Mon.{u}) :=
{ reflects := λ X Y f _,
  begin
    resetI,
    let i := as_iso ((forget Mon).map f),
    let e : X ≃* Y := { ..f, ..i.to_equiv },
    exact ⟨(is_iso.of_iso e.to_Mon_iso).1⟩,
  end }

@[to_additive]
instance CommMon.forget_reflects_isos : reflects_isomorphisms (forget CommMon.{u}) :=
{ reflects := λ X Y f _,
  begin
    resetI,
    let i := as_iso ((forget CommMon).map f),
    let e : X ≃* Y := { ..f, ..i.to_equiv },
    exact ⟨(is_iso.of_iso e.to_CommMon_iso).1⟩,
  end }

/-!
Once we've shown that the forgetful functors to type reflect isomorphisms,
we automatically obtain that the `forget₂` functors between our concrete categories
reflect isomorphisms.
-/
example : reflects_isomorphisms (forget₂ CommMon Mon) := by apply_instance


import CvxLean.Tactic.DCP.Atoms
import CvxLean.Lib.Cones
import CvxLean.Lib.Missing.Real
import CvxLean.Lib.Missing.Matrix
import CvxLean.Syntax.Minimization
import Optbin.All
import Mathbin.Algebra.Order.MonoidLemmasZeroLt
import Mathbin.Algebra.Order.Group

namespace CvxLean

-- Constraints in conic form.
section Cones

open Real

-- Optimality for using a variable in the second argument
-- will be hard to prove optimality for.
declare_atom expCone [cone] (x : ℝ)- (z : ℝ)+ : expCone x 1 z :=
optimality by
  intros x' z' hx hz hexp
  rw [←exp_iff_expCone] at *
  exact ((exp_le_exp.2 hx).transₓ hexp).transₓ hz

declare_atom Vec.expCone [cone] (n : Nat)& (x : (Finₓ n) → ℝ)- (z : (Finₓ n) → ℝ)+ : Vec.expCone x 1 z :=
optimality by
  intros x' z' hx hz hexp i
  unfold Vec.expCone at *
  apply (exp_iff_expCone _ _).1
  exact ((exp_le_exp.2 (hx i)).transₓ ((exp_iff_expCone _ _).2 (hexp i))).transₓ (hz i)

declare_atom posOrthCone [cone] (n : Nat)& (x : ℝ)+ : posOrthCone x :=
optimality by
  intros x' hx hx0
  exact hx0.transₓ hx

declare_atom Vec.posOrthCone [cone] (n : Nat)& (x : (Finₓ n) → ℝ)+ : Vec.posOrthCone x :=
optimality by
  intros x' hx hx0 i
  exact (hx0 i).transₓ (hx i)

declare_atom Matrix.posOrthCone [cone] (m : Nat)& (n : Nat)& (M : Matrix.{0,0,0} (Finₓ m) (Finₓ n) ℝ)+ :
  Real.Matrix.posOrthCone M :=
optimality by
  intros x' hx hx0 i j
  exact (hx0 i j).transₓ (hx i j)

declare_atom rotatedSoCone [cone] (n : Nat)& (v : ℝ)+ (w : ℝ)+ (x : (Finₓ n) → ℝ)? :
  rotatedSoCone v w x :=
optimality by
  intros v' w' hv hw h
  unfold rotatedSoCone at *
  apply And.intro
  · apply h.1.transₓ
    apply ZeroLt.mul_le_mul_of_nonneg_right
    apply ZeroLt.mul_le_mul_of_le_of_le hv hw h.2.1 (h.2.2.transₓ hw)
    have : @Zero.zero Real (MulZeroClassₓ.toHasZero Real) = 0 := by
      rfl
    rw [this]
    simp only [(@Nat.cast_zero ℝ _).symm, (@Nat.cast_one ℝ _).symm]
    apply Nat.cast_le.2
    norm_num
  · exact ⟨h.2.1.transₓ hv, h.2.2.transₓ hw⟩

declare_atom Vec.rotatedSoCone [cone] (m : Nat)& (n : Nat)& (v : (Finₓ n) → ℝ)+ (w : (Finₓ n) → ℝ)+ (x : (Finₓ n) → (Finₓ m) → ℝ)? :
  Vec.rotatedSoCone v w x :=
optimality by
  unfold Vec.rotatedSoCone
  intros v' w' hv hw h i
  apply rotatedSoCone.optimality _ _ _ _ _ _ (hv i) (hw i) (h i)

declare_atom Matrix.PSDCone [cone] (m : Type)& (hm : Fintype.{0} m)& (A : Matrix.{0,0,0} m m ℝ)? :
  Matrix.PSDCone A :=
optimality fun h => h

end Cones

-- NOTE: Workaround for nonterminating simp.
attribute [-simp] Quot.lift_on_mk Quot.lift_on₂_mk Quot.lift₂_mk

-- Affine operations.
section RealAffine

open Real

declare_atom add [affine] (x : ℝ)+ (y : ℝ)+ : x + y :=
bconditions
homogenity by
  change _ = _ + _ + HasSmul.smul κ (Zero.zero + Zero.zero)
  rw [smul_add, smul_add, smul_zero]
  rfl
additivity by
  simp only [add_zero, add_assoc, add_commₓ]
  rw [add_commₓ x' y', ←add_assocₓ y y' x', add_commₓ _ x']
optimality fun _ _ => add_le_add

declare_atom neg [affine] (x : ℝ)- : - x :=
bconditions
homogenity by
  change HasSmul.smul κ (-x) + -Zero.zero =
    -HasSmul.smul κ x + HasSmul.smul κ (-Zero.zero)
  simp
  rw [smul_zero, smul_neg, add_zeroₓ]
  rfl
additivity by
  rw [neg_add]
  simp
  rfl
optimality by
  intros x' hx
  apply neg_le_neg hx

declare_atom maximizeNeg [affine] (x : ℝ)- : maximizeNeg x :=
bconditions
homogenity by
  change HasSmul.smul κ (-x) + -Zero.zero =
    -HasSmul.smul κ x + HasSmul.smul κ (-Zero.zero)
  simp
  rw [smul_zero, smul_neg, add_zeroₓ]
  rfl
additivity by
  unfold maximizeNeg
  rw [neg_add]
  simp
  rfl
optimality by
  intros x' hx
  apply neg_le_neg hx

declare_atom sub [affine] (x : ℝ)+ (y : ℝ)- : x - y :=
bconditions
homogenity by
  rw [smul_sub, sub_self, smul_zero]
  rfl
additivity by
  rw [sub_add_comm, add_commₓ, add_sub, add_sub_assoc, sub_sub, add_commₓ y' y,
    add_sub, sub_self, add_zeroₓ]
optimality by
  intros x' y' hx hy
  apply @sub_le_sub Real _ _
    (@OrderedAddCommGroup.to_covariant_class_left_le Real Real.orderedAddCommGroup)
  exact hx
  exact hy

declare_atom mul1 [affine] (x : ℝ)& (y : ℝ)+ : x * y :=
bconditions (hx : 0 ≤ x)
homogenity by
  change HasSmul.smul κ (x * y) + x * Zero.zero
    = x * HasSmul.smul κ y + HasSmul.smul κ (x * Zero.zero)
  rw [_root_.mul_zero, smul_zero, add_zeroₓ, add_zeroₓ, mul_smul_comm]
additivity by
  change x * y + x * y' = x * (y + y') + x * Zero.zero
  rw [mul_addₓ, _root_.mul_zero, add_zeroₓ]
optimality by
  intros y' hy
  apply ZeroLt.mul_le_mul_of_nonneg_left hy hx

declare_atom mul2 [affine] (x : ℝ)+ (y : ℝ)& : x * y :=
bconditions (hy : 0 ≤ y)
homogenity by
  change HasSmul.smul κ (x * y) + Zero.zero * y
    = (HasSmul.smul κ x) * y + HasSmul.smul κ (Zero.zero * y)
  rw [_root_.zero_mul, smul_zero, add_zeroₓ, add_zeroₓ,
    _root_.mul_comm, _root_.mul_comm _ y, mul_smul_comm]
additivity by
  change x * y + x' * y = (x + x') * y + Zero.zero * y
  rw [add_mulₓ, _root_.zero_mul, add_zeroₓ]
optimality by
  intros y' hx
  apply ZeroLt.mul_le_mul_of_nonneg_right hx hy

end RealAffine

-- Affine operations on vectors.
section VecAffine

declare_atom Vec.nth [affine] (m : Nat)&  (x : Finₓ m → ℝ)? (i : Finₓ m)& : x i :=
bconditions
homogenity by
  change HasSmul.smul κ (x i) + Zero.zero
    = HasSmul.smul κ x i + HasSmul.smul κ Zero.zero
  rw [smul_zero, add_zeroₓ, add_zeroₓ]
  rfl
additivity by
  change x i + x' i = x i + x' i + Zero.zero
  rw [add_zeroₓ]
optimality le_reflₓ _

declare_atom Vec.add [affine] (m : Nat)&  (x : Finₓ m → ℝ)+ (y : Finₓ m → ℝ)+ : x + y :=
bconditions
homogenity by
  change HasSmul.smul κ (x + y) + (Zero.zero + Zero.zero)
    = HasSmul.smul κ x + HasSmul.smul κ y + HasSmul.smul κ (Zero.zero + Zero.zero)
  rw [add_zeroₓ, add_zeroₓ, smul_zero, add_zeroₓ, smul_add]
  rfl
additivity by
  change x + y + (x' + y') = x + x' + (y + y') + (Zero.zero + Zero.zero)
  rw [add_zeroₓ, add_zeroₓ, add_assocₓ, add_commₓ x' y', ←add_assocₓ y, add_commₓ _ x']
  simp [add_assocₓ]
optimality by
  intros x' y' hx hy i
  apply add_le_add (hx i) (hy i)

declare_atom Vec.sub [affine] (m : Nat)&  (x : Finₓ m → ℝ)+ (y : Finₓ m → ℝ)- : x - y :=
bconditions
homogenity by
  rw [sub_self, smul_sub, smul_zero]
  rfl
additivity by
  rw [sub_add_sub_comm, sub_self, add_zeroₓ]
optimality by
  intros x' y' hx hy i
  apply @sub_le_sub Real _ _
    (@OrderedAddCommGroup.to_covariant_class_left_le Real Real.orderedAddCommGroup)
  exact (hx i)
  exact (hy i)

declare_atom Vec.sum [affine] (m : Nat)& (x : Finₓ m → ℝ)+ : Vec.sum x :=
bconditions
homogenity by
  change HasSmul.smul κ (Vec.sum x) + (∑ i, Zero.zero)
    = Vec.sum (HasSmul.smul κ x) + HasSmul.smul κ (∑ i, Zero.zero)
  unfold Vec.sum
  rw [Finset.smul_sum, Finset.sum_const_zero, add_zeroₓ, smul_zero, add_zeroₓ]
  rfl
additivity by
  change (∑ i, x i) + (∑ i, x' i) = (∑ i, x i + x' i) + ∑ i, Zero.zero
  rw [Finset.sum_const_zero, add_zeroₓ, Finset.sum_add_distrib]
optimality by
  intro x' hx
  apply Finset.sum_le_sum
  intros
  apply hx

declare_atom div [affine] (x : ℝ)+ (y : ℝ)& : x / y :=
bconditions (hy : (0 : ℝ) ≤ y)
homogenity by
  change κ * (x / y) + Zero.zero / y
    = κ * x / y + κ * (Zero.zero / y)
  rw [zero_div, add_zeroₓ, _root_.mul_zero, add_zeroₓ, mul_div]
additivity by
  change x / y + x' / y = (x + x') / y + Zero.zero / y
  rw [zero_div, add_zeroₓ, add_div]
optimality by
  intros x' hx
  by_cases h : Zero.zero = y
  · rw [←h, div_zero, div_zero]
    exact le_reflₓ _
  · rw [div_le_div_right]
    apply hx
    apply lt_of_le_of_neₓ hy h

declare_atom Vec.dotProduct1 [affine] (m : Nat)& (x : Finₓ m → ℝ)& (y : Finₓ m → ℝ)? : Matrix.dotProduct x y :=
bconditions
homogenity by
  change κ * (Matrix.dotProduct x y) + Matrix.dotProduct x Zero.zero =
    Matrix.dotProduct x (HasSmul.smul κ y) + κ * (Matrix.dotProduct x Zero.zero)
  rw [Matrix.dot_product_zero, add_zeroₓ, _root_.mul_zero, add_zeroₓ,
    Matrix.dot_product_smul]
  rfl
additivity by
  change Matrix.dotProduct x y + Matrix.dotProduct x y'
    = Matrix.dotProduct x (y + y') + Matrix.dotProduct x Zero.zero
  rw [Matrix.dot_product_zero, add_zeroₓ, Matrix.dot_product_add]
optimality le_reflₓ _

declare_atom Vec.dotProduct2 [affine] (m : Nat)& (x : Finₓ m → ℝ)? (y : Finₓ m → ℝ)& : Matrix.dotProduct x y :=
bconditions
homogenity by
  change κ * (Matrix.dotProduct x y) + Matrix.dotProduct Zero.zero y =
    Matrix.dotProduct (HasSmul.smul κ x) y + κ * (Matrix.dotProduct Zero.zero y)
  rw [Matrix.zero_dot_product, add_zeroₓ, _root_.mul_zero, add_zeroₓ,
    Matrix.dot_product_comm (HasSmul.smul κ x), Matrix.dot_product_smul, Matrix.dot_product_comm]
  rfl
additivity by
  change Matrix.dotProduct x y + Matrix.dotProduct x' y
    = Matrix.dotProduct (x + x' : Finₓ m → Real) y + Matrix.dotProduct Zero.zero y
  rw [Matrix.zero_dot_product, add_zeroₓ, Matrix.add_dot_product]
optimality le_reflₓ _

declare_atom smul [affine] (n : ℕ)& (y : ℝ)+ : @HasSmul.smul ℕ Real AddMonoidₓ.hasSmulNat n y :=
bconditions
homogenity by
  change HasSmul.smul κ (HasSmul.smul n y) + HasSmul.smul n Zero.zero =
    HasSmul.smul n (HasSmul.smul κ y) + HasSmul.smul κ (HasSmul.smul n Zero.zero)
  rw [smul_zero, add_zeroₓ, smul_zero, smul_zero, add_zeroₓ, smul_comm]
additivity by
  change HasSmul.smul n y + HasSmul.smul n y'
    = HasSmul.smul n (y + y') + HasSmul.smul n Zero.zero
  rw [smul_zero, add_zeroₓ, smul_add]
  rfl
optimality by
  intros y' hy
  apply smul_le_smul_of_nonneg hy (Nat.zero_leₓ _)

end VecAffine

-- Affine operations on matrices.
section MatrixAffine

declare_atom Matrix.vec_cons [affine] (n : Nat)& (x : ℝ)+ (y : (Finₓ n) → ℝ)+ :
  Matrix.vecCons x y :=
bconditions
homogenity by
  change HasSmul.smul κ (Matrix.vecCons x y) + Matrix.vecCons (Zero.zero : ℝ) (Zero.zero : (Finₓ n) → ℝ) =
    Matrix.vecCons (κ * x) (HasSmul.smul κ y) + HasSmul.smul κ (Matrix.vecCons (Zero.zero : ℝ) (Zero.zero : (Finₓ n) → ℝ))
  ext ⟨i, hi⟩
  cases i
  · simp [HasSmul.smul, Matrix.Computable.Pi.hasSmul', Matrix.Computable.Mul.toHasSmul']
  · simp [HasSmul.smul, Matrix.Computable.Pi.hasSmul', Matrix.Computable.Mul.toHasSmul']
additivity by
  change Matrix.vecCons x y + Matrix.vecCons x' y'
    = Matrix.vecCons (x + x' : ℝ) (y + y' : (Finₓ n) → ℝ)
      + Matrix.vecCons (Zero.zero : ℝ) (Zero.zero : (Finₓ n) → ℝ)
  ext ⟨i, hi⟩
  cases i
  · simp
  · simp
optimality by
  intros x' y' hx hy i
  cases i with
  | mk i hi =>
    cases i with
    | zero => simp [hx]
    | succ i => simp [hy ⟨i, Nat.succ_lt_succ_iff.1 hi⟩]

declare_atom Matrix.sum [affine] (m : Nat)& (X : Matrix.{0,0,0} (Finₓ m) (Finₓ m) ℝ)+ : Matrix.sum X :=
bconditions
homogenity by
  change κ * (∑ i, ∑ j, X i j) + (∑ i, ∑ j, Zero.zero)
    = (∑ i, ∑ j, κ * X i j) + κ * (∑ i, ∑ j, Zero.zero)
  rw [Finset.sum_const_zero, Finset.sum_const_zero, _root_.mul_zero, add_zeroₓ, add_zeroₓ]
  simp only [Finset.mul_sum]
additivity by
  change (∑ i, ∑ j, X i j) + (∑ i, ∑ j, X' i j)
    = (∑ i, ∑ j, X i j + X' i j) + ∑ i, ∑ j, Zero.zero
  rw [Finset.sum_const_zero, Finset.sum_const_zero, add_zeroₓ]
  simp only [Finset.sum_add_distrib]
optimality by
  intros X' hX
  apply Finset.sum_le_sum (fun i _ => Finset.sum_le_sum (fun j _ => ?_))
  apply hX

declare_atom Matrix.nth [affine] (m : Nat)& (X : Matrix.{0,0,0} (Finₓ m) (Finₓ m) ℝ)? (i : Finₓ m)& : X i :=
bconditions
homogenity by
  change HasSmul.smul κ (X i) + Zero.zero = HasSmul.smul κ X i + HasSmul.smul κ Zero.zero
  rw [add_zeroₓ, smul_zero, add_zeroₓ]
  rfl
additivity by
  change X i + X' i = HAdd.hAdd X X' i + Zero.zero
  rw [add_zeroₓ]
  rfl
optimality le_reflₓ _

declare_atom Matrix.nth2 [affine] (m : Nat)& (X : Matrix.{0,0,0} (Finₓ m) (Finₓ m) ℝ)? (i : Finₓ m)& (j : Finₓ m)& : X i j :=
bconditions
homogenity by
  change _ + Zero.zero = _ + HasSmul.smul κ Zero.zero
  rw [smul_zero]
  rfl
additivity by
  change _ = _ + Zero.zero
  rw [add_zeroₓ]
  rfl
optimality le_reflₓ _

-- TODO: make argument increasing, without breaking det-log-atom
declare_atom Matrix.diag [affine] (n : ℕ)& (A : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)? : A.diag :=
bconditions
homogenity by
  change _ + Zero.zero
    = _ + HasSmul.smul κ Zero.zero
  rw [add_zeroₓ, smul_zero, add_zeroₓ]
  rfl
additivity by
  change _  = _ + Zero.zero
  rw [add_zeroₓ]
  rfl
optimality le_reflₓ _

declare_atom Matrix.diagonal [affine] (n : ℕ)& (d : Finₓ n → ℝ)+ : Matrix.diagonal d :=
bconditions
homogenity by
  ext i j
  change
    κ * (Matrix.diagonalₓ d) i j + (Matrix.diagonal fun i => Zero.zero) i j =
    Matrix.diagonalₓ (HasSmul.smul κ d) i j + HasSmul.smul κ (Matrix.diagonal Zero.zero) i j
  by_cases h : i = j
  · simp [Matrix.diagonal, h]
    change κ * d j = HasSmul.smul κ d j + κ * Zero.zero
    rw [_root_.mul_zero, add_zeroₓ]
    rfl
  · simp [Matrix.diagonal, h]
    change Zero.zero = κ * Zero.zero
    rw [_root_.mul_zero]
additivity by
  change Matrix.diagonalₓ _ + Matrix.diagonalₓ _
    = _ + Matrix.diagonalₓ fun i => Zero.zero
  rw [Matrix.diagonal_add, Matrix.diagonal_zero, add_zeroₓ]
  rfl
optimality by
  intros d' hd i j
  by_cases h : i = j
  · simp [Matrix.diagonal, h, hd j]
  · simp [Matrix.diagonal, h, hd j]

declare_atom Matrix.diagonalₓ [affine] (n : ℕ)& (d : Finₓ n → ℝ)+ : Matrix.diagonalₓ d :=
bconditions
homogenity by
  ext i j
  change
    κ * (Matrix.diagonalₓ d) i j + (Matrix.diagonal fun i => Zero.zero) i j =
    Matrix.diagonalₓ (HasSmul.smul κ d) i j + HasSmul.smul κ (Matrix.diagonal Zero.zero) i j
  by_cases h : i = j
  · simp [Matrix.diagonal, h]
    change κ * d j = HasSmul.smul κ d j + κ * Zero.zero
    rw [_root_.mul_zero, add_zeroₓ]
    rfl
  · simp [Matrix.diagonal, h]
    change Zero.zero = κ * Zero.zero
    rw [_root_.mul_zero]
additivity by
  change _ = _ + Matrix.diagonalₓ fun i => Zero.zero
  rw [Matrix.diagonal_add, Matrix.diagonal_zero, add_zeroₓ]
  rfl
optimality by
  intros d' hd i j
  by_cases h : i = j
  · simp [Matrix.diagonal, h, hd j]
  · simp [Matrix.diagonal, h, hd j]

-- NOTE: Helper lemma needed due to mathport
lemma zero_eq_zero [Zero α] : (0 : α) = Zero.zero := rfl

declare_atom Matrix.trace [affine] (m : Type)& (hm : Fintype.{0} m)& (A : Matrix.{0,0,0} m m ℝ)+ : Matrix.trace A:=
bconditions
homogenity by
  change HasSmul.smul κ (Matrix.trace A) + Matrix.trace Zero.zero
    = Matrix.trace (HasSmul.smul κ A) + HasSmul.smul κ (Matrix.trace Zero.zero)
  rw [← Matrix.trace_smul, ← Matrix.trace_smul, smul_zero]
  rfl
additivity by
  rw [zero_eq_zero, Matrix.trace_add, Matrix.trace_zero, add_zeroₓ]
optimality by
  intros A' hA
  apply Finset.sum_le_sum
  intros i _
  exact hA i i

declare_atom Matrix.toUpperTri [affine] (n : ℕ)& (A : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+ :
  A.toUpperTri :=
bconditions
homogenity by
  ext i j
  change κ * (Matrix.toUpperTri A) i j + (Matrix.toUpperTri 0) i j =
    (Matrix.toUpperTri (HasSmul.smul κ A)) i j + κ * (Matrix.toUpperTri Zero.zero) i j
  by_cases h : i ≤ j
  · unfold Matrix.toUpperTri
    simp [h]
    change κ * A i j + 0 = HasSmul.smul κ A i j + κ * Zero.zero
    rw [_root_.mul_zero]
    rfl
  · unfold Matrix.toUpperTri
    simp [h]
additivity by
  ext i j
  change (Matrix.toUpperTri A) i j+ (Matrix.toUpperTri A') i j =
    (Matrix.toUpperTri _) i j + (Matrix.toUpperTri Zero.zero) i j
  by_cases h : i ≤ j
  · unfold Matrix.toUpperTri
    simp [h]
    change A i j + A' i j = HAdd.hAdd A A' i j + Zero.zero
    rw [add_zeroₓ]
    rfl
  · unfold Matrix.toUpperTri
    simp [h]
optimality by
  intros A' hA
  ext i j
  by_cases h : i ≤ j
  · unfold Matrix.toUpperTri
    simp [h, hA i j]
  · unfold Matrix.toUpperTri
    simp [h]

declare_atom Matrix.transpose [affine] (n : ℕ)& (A : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+ :
  A.transpose :=
bconditions
homogenity by
  change _ = _ + HasSmul.smul _ (Matrix.transposeₓ Zero.zero)
  simp [zero_eq_zero, Matrix.transpose_zero]
  rw [smul_zero]
  rfl
additivity by
  change Matrix.transposeₓ _ + Matrix.transposeₓ _
    = Matrix.transposeₓ _ + Matrix.transposeₓ _
  simp
  rfl
optimality by
  intros _ hA
  ext i j
  exact hA j i

declare_atom Matrix.transposeₓ [affine] (n : ℕ)& (A : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+ :
  A.transposeₓ :=
bconditions
homogenity by
  simp [zero_eq_zero, Matrix.transpose_zero]
  rw [smul_zero, add_zeroₓ]
  rfl
additivity by
  simp
  rfl
optimality by
  intros _ hA
  ext i j
  exact hA j i

@[simp] lemma Matrix.from_blocks_zero [Zero α]:
  Matrix.fromBlocks (0 : Matrix n l α) (0 : Matrix n m α) (0 : Matrix o l α) (0 : Matrix o m α) = 0 := by
  ext i j
  cases i
  · cases j
    rfl
    rfl
  · cases j
    rfl
    rfl

declare_atom Matrix.fromBlocks [affine] (n : ℕ)&
  (A : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+ (B : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+
  (C : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+ (D : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)+ :
  Matrix.fromBlocks A B C D :=
bconditions
homogenity by
  change _ = _ + HasSmul.smul κ _
  rw [Matrix.from_blocks_smul,  Matrix.from_blocks_zero, zero_eq_zero, smul_zero]
  rfl
additivity by
  simp [Matrix.from_blocks_add, zero_eq_zero]
optimality by
  intros A' B' C' D' hA hB hC hD i j
  cases i with
  | inl i =>
    cases j with
    | inl j => exact hA i j
    | inr j => exact hB i j
  | inr i =>
    cases j with
    | inl j => exact hC i j
    | inr j => exact hD i j

declare_atom Matrix.add [affine] (m : Type)& (n : Type)& (A : Matrix.{0,0,0} m n ℝ)+ (B : Matrix.{0,0,0} m n ℝ)+ : A + B :=
bconditions
homogenity by
  rw [zero_eq_zero, add_zeroₓ, add_zeroₓ, smul_zero, add_zeroₓ, smul_add]
  rfl
additivity by
  rw [zero_eq_zero, add_zeroₓ, add_zeroₓ, add_assocₓ, add_commₓ B, add_assocₓ A', add_commₓ B']
  simp only [add_assocₓ]
optimality by
  intros A' B' hA hB i j
  apply add_le_add (hA i j) (hB i j)

declare_atom Matrix.sub [affine] (m : Type)& (n : Type)& (A : Matrix.{0,0,0} m n ℝ)+ (B : Matrix.{0,0,0} m n ℝ)- : A - B :=
bconditions
homogenity by
  ext i j
  rw [sub_self]
  change
    κ * (A - B) i j + Zero.zero =
    κ * A i j - κ * B i j + κ * Zero.zero
  rw [_root_.mul_zero, add_zeroₓ, add_zeroₓ, ←mul_sub]
  rfl
additivity by
  rw [sub_self, add_zeroₓ, sub_add_sub_comm]
optimality by
  intros A' B' hA hB i j
  change A i j - B i j ≤ A' i j - B' i j
  apply @sub_le_sub Real _ _
    (@OrderedAddCommGroup.to_covariant_class_left_le Real Real.orderedAddCommGroup)
  exact hA i j
  exact hB i j

declare_atom Matrix.mul1 [affine] (m : Type)& (hm : Fintype.{0} m)&
  (A : Matrix.{0,0,0} m m ℝ)& (B : Matrix.{0,0,0} m m ℝ)? : A ⬝ B :=
bconditions
homogenity by
  rw [zero_eq_zero, Matrix.mul_zero, smul_zero, Matrix.mul_smul]
  rfl
additivity by
  rw [Matrix.mul_add, zero_eq_zero, Matrix.mul_zero, add_zeroₓ]
optimality le_reflₓ (A ⬝ B)

declare_atom Matrix.mul2 [affine] (m : Type)& (hm : Fintype.{0} m)&
  (A : Matrix.{0,0,0} m m ℝ)? (B : Matrix.{0,0,0} m m ℝ)& : A ⬝ B :=
bconditions
homogenity by
  haveI := @IsScalarTower.right Real Real Real.commSemiring Real.semiring (@Algebra.id Real Real.commSemiring)
  rw [Matrix.smul_mul]
  rw [zero_eq_zero, Matrix.zero_mul, smul_zero]
  rfl
additivity by
  rw [Matrix.add_mul, zero_eq_zero, Matrix.zero_mul, add_zeroₓ]
optimality le_reflₓ (A ⬝ B)

declare_atom Matrix.mulVec [affine] (n : ℕ)& (m : ℕ)& (M : Matrix.{0,0,0} (Finₓ m) (Finₓ n) ℝ)& (v : Finₓ n → ℝ)? :
  Matrix.mulVecₓ M v :=
bconditions
homogenity by
  simp [zero_eq_zero]
  rw [smul_zero, add_zeroₓ, Matrix.mul_vec_smul]
  rfl
additivity by
  simp [zero_eq_zero, Matrix.mul_vec_add]
optimality le_reflₓ _

declare_atom Matrix.vecMul [affine] (n : ℕ)& (m : ℕ)& (v : Finₓ m → ℝ)? (M : Matrix.{0,0,0} (Finₓ m) (Finₓ n) ℝ)& :
  Matrix.vecMulₓ v M :=
bconditions
homogenity by
  simp [zero_eq_zero]
  haveI := @IsScalarTower.right Real Real Real.commSemiring Real.semiring (@Algebra.id Real Real.commSemiring)
  rw [smul_zero, add_zeroₓ, Matrix.vec_mul_smul]
  rfl
additivity by
  simp [zero_eq_zero, Matrix.add_vec_mul]
optimality le_reflₓ _

end MatrixAffine

-- Non-affine atoms on real variables.
section Real

open Real

declare_atom le [concave] (x : ℝ)- (y : ℝ)+ : x ≤ y :=
vconditions
implementationVars
implementationObjective Real.posOrthCone (y - x)
implementationConstraints
solution
solutionEqualsAtom by
  simp [Real.posOrthCone, zero_eq_zero]
feasibility
optimality by
  intros x' y' hx hy h
  simp [Real.posOrthCone, zero_eq_zero] at h
  exact (hx.transₓ h).transₓ hy
vconditionElimination

declare_atom eq [concave] (x : ℝ)? (y : ℝ)? : x = y :=
vconditions
implementationVars
implementationObjective Real.zeroCone (y - x)
implementationConstraints
solution
solutionEqualsAtom by
  simp [Real.zeroCone, sub_eq_iff_eq_add, zero_add]
  exact Iff.intro Eq.symm Eq.symm;
feasibility
optimality by
  simp [Real.zeroCone, sub_eq_iff_eq_add, zero_add]
  intros h
  exact Eq.symm h
vconditionElimination

declare_atom sq [convex] (x : ℝ)? : x ^ 2 :=
vconditions
implementationVars (t : ℝ)
implementationObjective (t)
implementationConstraints
  (c1 : rotatedSoCone t (1/2) (![x] : Finₓ 1 → ℝ))
solution
  (t := x ^ 2)
solutionEqualsAtom rfl
feasibility
  (c1 : by
    simp [rotatedSoCone]
    refine ⟨?_, ?_, ?_⟩
    · have : (2 : ℝ) ≠ 0 := by
        apply ne_of_gtₓ
        simp only [(@Nat.cast_zero ℝ _).symm, (@Nat.cast_one ℝ _).symm]
        apply Nat.cast_lt.2
        norm_num
      simp [_root_.mul_assoc, div_mul_cancel _ this]
      have : x ^ 2 = @HPow.hPow ℝ ℕ ℝ instHPow x 2 :=
        by apply Real.rpow_nat_cast
      rw [this]
      exact le_reflₓ _
    · have : x ^ 2 = @HPow.hPow ℝ ℕ ℝ instHPow x 2 :=
        by apply Real.rpow_nat_cast
      rw [this]
      exact sq_nonneg x
    · rw [zero_eq_zero]
      rw [← zero_div 2]
      have : (0 : ℝ) < (2 : ℝ) := by
        simp only [(@Nat.cast_zero ℝ _).symm, (@Nat.cast_one ℝ _).symm]
        apply Nat.cast_lt.2
        norm_num
      have := (@div_le_div_right ℝ _ 0 1 2 this).2
      refine this ?_
      have : ZeroLeOneClass ℝ := @OrderedSemiring.zeroLeOneClass ℝ Real.orderedSemiring
      exact zero_le_one)
optimality by
  have := c1.1
  have two_ne_zero : (2 : ℝ) ≠ 0 := by
    apply ne_of_gtₓ
    simp only [(@Nat.cast_zero ℝ _).symm, (@Nat.cast_one ℝ _).symm]
    apply Nat.cast_lt.2
    norm_num
  simp [_root_.mul_assoc, div_mul_cancel _ two_ne_zero] at this
  have pow_eq_pow : x ^ 2 = @HPow.hPow ℝ ℕ ℝ instHPow x 2 :=
    by apply Real.rpow_nat_cast
  rw [pow_eq_pow]
  exact this
vconditionElimination

declare_atom exp [convex] (x : ℝ)+ : Real.exp x :=
vconditions
implementationVars (t : ℝ)
implementationObjective t
implementationConstraints (c_exp : expCone x 1 t)
solution (t := exp x)
solutionEqualsAtom by
  rfl;
feasibility (c_exp : by
  simp [expCone]
  apply Or.inl;
  refine ⟨Real.zero_lt_one, ?_⟩
  change x / One.one ≤ x
  rw [div_one]
  apply le_reflₓ _)
optimality by
  intros x' hx
  rw [←exp_iff_expCone] at c_exp
  exact (exp_le_exp.2 hx).transₓ c_exp
vconditionElimination

declare_atom sqrt [concave] (x : ℝ)+ : Real.sqrt x :=
vconditions (cond : 0 ≤ x)
implementationVars (t : ℝ)
implementationObjective (t)
implementationConstraints
  (c1 : rotatedSoCone x (1/2) (![t] : Finₓ 1 → ℝ))
solution (t := Real.sqrt x)
solutionEqualsAtom by
  rfl;
feasibility
  (c1 : by
    unfold rotatedSoCone
    rw [← sq_sqrt cond]
    have : sqrt x ^ 2 = @HPow.hPow ℝ ℕ ℝ instHPow (sqrt x) 2 :=
        by apply Real.rpow_nat_cast
    have sqf := sq.feasibility0 (sqrt x)
    rw [this] at sqf
    simp only [rotatedSoCone] at sqf
    have : 2 = bit0 One.one := rfl
    rw [this, sq_sqrt cond] at sqf
    rw [this, sq_sqrt cond]
    apply sqf)
optimality by
  intros y hy
  have sqopt := sq.optimality t x c1
  apply Real.le_sqrt_of_sq_le
  have : t ^ 2 = @HPow.hPow ℝ ℕ ℝ instHPow t (bit0 One.one) :=
      by apply Real.rpow_nat_cast
  rw [←this]
  apply sqopt.transₓ hy
vconditionElimination (cond : fun _ hx => c1.2.1.transₓ hx)

declare_atom log [concave] (x : ℝ)+ : log x :=
vconditions (cond : 0 < x)
implementationVars (t : ℝ)
implementationObjective t
implementationConstraints (c_exp : expCone t (1) x)
solution (t := log x)
solutionEqualsAtom by
  rfl;
feasibility (c_exp : by
  simp [expCone]
  left
  apply And.intro Real.zero_lt_one
  erw [div_one, Real.exp_log cond]
  exact le_reflₓ _)
optimality by
  intros x' hx;
  simp [expCone] at c_exp
  cases c_exp with
  | inl h =>
    rcases h with ⟨_, h⟩
    cases em (0 < x) with
    | inl h0x =>
      erw [le_log_iff_exp_le (lt_of_lt_of_leₓ h0x hx)]
      erw [div_one] at h
      exact le_transₓ h hx
    | inr h0x =>
      exfalso
      cases (eq_or_lt_of_not_ltₓ h0x) with
      | inl heq =>
        erw [div_one] at h
        exact lt_irreflₓ 0 (lt_of_lt_of_leₓ (exp_pos t) (heq ▸ h))
      | inr hlt =>
        erw [div_one] at h
        exact lt_irreflₓ 0 (lt_transₓ (lt_of_lt_of_leₓ (exp_pos t) h) hlt)
  | inr h =>
    rcases h with ⟨_, hc, _⟩;
    exfalso
    exact (zero_ne_one hc.symm)
vconditionElimination
  (cond : by
    simp [expCone] at c_exp
    apply c_exp.by_cases
    · intro h
      exact fun _ h' => lt_of_lt_of_leₓ (lt_of_lt_of_leₓ (Real.exp_pos _) h.2) h'
    · intro h
      exact False.elim $ zero_ne_one h.2.1.symm)

declare_atom abs [convex] (x : ℝ)? : abs x :=
vconditions
implementationVars (t : ℝ)
implementationObjective t
implementationConstraints
  (c_pos : posOrthCone (t - x))
  (c_neg : posOrthCone (t + x))
solution (t := abs x)
solutionEqualsAtom rfl
feasibility
  (c_pos : by
    unfold posOrthCone
    rw [zero_eq_zero, sub_nonneg]
    apply le_abs_self)
  (c_neg : by
    unfold posOrthCone
    rw [zero_eq_zero, ← neg_le_iff_add_nonneg'
      (_inst_3 := @OrderedAddCommGroup.to_covariant_class_left_le Real Real.orderedAddCommGroup)]
    apply neg_abs_le_self
      (_inst_3 := @OrderedAddCommGroup.to_covariant_class_left_le Real Real.orderedAddCommGroup))
optimality by
  apply abs_le.2
  rw [←sub_nonneg, sub_neg_eq_add, add_commₓ, ←sub_nonneg (b := x)]
  exact ⟨c_neg, c_pos⟩
vconditionElimination

end Real

-- Non-affine atoms on vectors.
section Vec

open Vec

declare_atom Vec.le [concave] (n : Nat)& (x : (Finₓ n) → ℝ)- (y : (Finₓ n) → ℝ)+ : x ≤ y :=
vconditions
implementationVars
implementationObjective Real.Vec.posOrthCone (y - x : (Finₓ n) → ℝ)
implementationConstraints
solution
solutionEqualsAtom by
  unfold Real.Vec.posOrthCone
  rw [← iff_iff_eq]
  constructor
  · intros h i
    rw [←le.solEqAtom]
    apply h
  · intros h i
    change Zero.zero ≤ y i - x i
    rw [le.solEqAtom]
    apply h
feasibility
optimality by
  intros x' y' hx hy h i
  apply le.optimality _ _ _ _ (hx i) (hy i) (h i)
vconditionElimination

declare_atom Vec.exp [convex] (n : Nat)& (x : (Finₓ n) → ℝ)+ : exp x :=
vconditions
implementationVars (t : Finₓ n → ℝ)
implementationObjective t
implementationConstraints (c_exp : Real.Vec.expCone x 1 t)
solution (t := exp x)
solutionEqualsAtom
  rfl
feasibility
  (c_exp: by
    intros _ _
    apply exp.feasibility0)
optimality by
  intros x' hx i
  apply exp.optimality _ _ (c_exp i) _ (hx i)
vconditionElimination

declare_atom Vec.log [concave] (n : Nat)& (x : (Finₓ n) → ℝ)+ : log x :=
vconditions (cond : ∀ i, 0 < x i)
implementationVars (t : (Finₓ n) → ℝ)
implementationObjective t
implementationConstraints (c_exp : Real.Vec.expCone t 1 x)
solution (t := log x)
solutionEqualsAtom rfl
feasibility
  (c_exp: by
    intros _ i
    apply log.feasibility0
    apply cond)
optimality by
  intros x' hx i
  apply log.optimality _ _ (c_exp i) _ (hx i)
vconditionElimination (cond : by
  intros x' hx i
  apply log.vcondElim0 _ _ (c_exp i) _ (hx i))

declare_atom Vec.abs [convex] (n : Nat)& (x : (Finₓ n) → ℝ)? : abs x :=
vconditions
implementationVars (t : (Finₓ n) → ℝ)
implementationObjective t
implementationConstraints
  (c_pos : Real.Vec.posOrthCone (t - x : (Finₓ n) → ℝ))
  (c_neg : Real.Vec.posOrthCone (t + x : (Finₓ n) → ℝ))
solution (t := abs x)
solutionEqualsAtom rfl
feasibility
  (c_pos : by
    intros _ _
    apply abs.feasibility0)
  (c_neg : by
    intros _ _
    apply abs.feasibility1)
optimality by
  intros i
  apply abs.optimality _ _ (c_pos i) (c_neg i)
vconditionElimination

end Vec

-- Non-affine atoms on real variables.
namespace Matrix

declare_atom Matrix.PosSemidef [concave] (m : Type)& (hm : Fintype.{0} m)& (A : Matrix.{0,0,0} m m ℝ)? : Matrix.PosSemidef A :=
vconditions
implementationVars
implementationObjective Real.Matrix.PSDCone A
implementationConstraints
solution
solutionEqualsAtom by simp [Real.Matrix.PSDCone]
feasibility
optimality by simp [Real.Matrix.PSDCone]
vconditionElimination

declare_atom Matrix.logDet [concave] (n : ℕ)& (A : Matrix.{0,0,0} (Finₓ n) (Finₓ n) ℝ)? : Real.log A.det :=
vconditions (hA : A.PosDef)
implementationVars (t : Finₓ n → ℝ) (Y : Matrix (Finₓ n) (Finₓ n) ℝ)
-- The lower left values of `Y` are unused. CVXPy uses a vector `z` instead of a matrix `Y`.
implementationObjective Vec.sum t
implementationConstraints
  (c_exp : Real.Vec.expCone t 1 Y.diag)
  (c_posdef : Matrix.PosSemidef $
    let Z := Y.toUpperTri;
    let D := Matrix.diagonalₓ Y.diag
    let X := Matrix.fromBlocks D            Z
                               Z.transpose  A;
    X)
solution
  (t :=
    have : Decidable (A.PosDef) := Classical.dec _
    if h : A.PosDef then Vec.log (LDL.diagEntries h) else 0)
  (Y :=
    have : Decidable (A.PosDef) := Classical.dec _
    if h : A.PosDef then LDL.diag h ⬝ (LDL.lower h).transpose else 0)
solutionEqualsAtom by
  simp only [dif_pos hA, Vec.sum, Vec.log]
  exact Matrix.LogDetAtom.solution_eq_atom hA
feasibility
  (c_exp : by
    simp only [Real.Vec.expCone, dif_pos hA]
    intro i
    show
      Real.expCone ((Real.log (LDL.diagEntries hA i))) 1
        (Matrix.diag (LDL.diag hA ⬝ (LDL.lower hA).transpose) i)
    rw [← Real.exp_iff_expCone, Real.exp_log]
    exact Matrix.LogDetAtom.feasibility_exp hA i
    exact Matrix.LDL.diag_entries_pos hA i)
  (c_posdef : by
    simp only [dif_pos hA]
    apply Matrix.LogDetAtom.feasibility_pos_def' hA rfl rfl rfl)
optimality by
  apply Matrix.LogDetAtom.optimality _ rfl rfl c_posdef
  intro i
  rw [Real.exp_iff_expCone]
  apply c_exp
vconditionElimination
  (hA : by
    apply Matrix.LogDetAtom.cond_elim _ rfl rfl c_posdef
    · exact t
    · intro i
      rw [Real.exp_iff_expCone]
      apply c_exp)

declare_atom Matrix.abs [convex] (m : Nat)& (n : Nat)& (M : Matrix.{0,0,0} (Finₓ m) (Finₓ n) ℝ)? : Matrix.abs M :=
vconditions
implementationVars (T : Matrix (Finₓ m) (Finₓ n) ℝ)
implementationObjective T
implementationConstraints
  (c_pos : Real.Matrix.posOrthCone (T - M : Matrix (Finₓ m) (Finₓ n) ℝ))
  (c_neg : Real.Matrix.posOrthCone (T + M : Matrix (Finₓ m) (Finₓ n) ℝ))
solution (T := M.abs)
solutionEqualsAtom rfl
feasibility
  (c_pos : by
    intros _ _ _
    apply abs.feasibility0)
  (c_neg :  by
    intros _ _ _
    apply abs.feasibility1)
optimality by
  intros i j
  apply abs.optimality _ _ (c_pos i j) (c_neg i j)
vconditionElimination

end Matrix