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

universes u_1

namespace Mathlib

/-!
# The powerset of a finset
-/

namespace finset

/-! ### powerset -/

/-- When `s` is a finset, `s.powerset` is the finset of all subsets of `s` (seen as finsets). -/
def powerset {α : Type u_1} (s : finset α) : finset (finset α) :=
  mk (multiset.pmap mk (multiset.powerset (val s)) sorry) sorry

@[simp] theorem mem_powerset {α : Type u_1} {s : finset α} {t : finset α} : s ∈ powerset t ↔ s ⊆ t := sorry

@[simp] theorem empty_mem_powerset {α : Type u_1} (s : finset α) : ∅ ∈ powerset s :=
  iff.mpr mem_powerset (empty_subset s)

@[simp] theorem mem_powerset_self {α : Type u_1} (s : finset α) : s ∈ powerset s :=
  iff.mpr mem_powerset (subset.refl s)

@[simp] theorem powerset_empty {α : Type u_1} : powerset ∅ = singleton ∅ :=
  rfl

@[simp] theorem powerset_mono {α : Type u_1} {s : finset α} {t : finset α} : powerset s ⊆ powerset t ↔ s ⊆ t := sorry

@[simp] theorem card_powerset {α : Type u_1} (s : finset α) : card (powerset s) = bit0 1 ^ card s :=
  Eq.trans (multiset.card_pmap mk (multiset.powerset (val s)) (powerset._proof_1 s)) (multiset.card_powerset (val s))

theorem not_mem_of_mem_powerset_of_not_mem {α : Type u_1} {s : finset α} {t : finset α} {a : α} (ht : t ∈ powerset s) (h : ¬a ∈ s) : ¬a ∈ t :=
  mt (iff.mp mem_powerset ht a) h

theorem powerset_insert {α : Type u_1} [DecidableEq α] (s : finset α) (a : α) : powerset (insert a s) = powerset s ∪ image (insert a) (powerset s) := sorry

/-- Given an integer `n` and a finset `s`, then `powerset_len n s` is the finset of subsets of `s`
of cardinality `n`.-/
def powerset_len {α : Type u_1} (n : ℕ) (s : finset α) : finset (finset α) :=
  mk (multiset.pmap mk (multiset.powerset_len n (val s)) sorry) sorry

theorem mem_powerset_len {α : Type u_1} {n : ℕ} {s : finset α} {t : finset α} : s ∈ powerset_len n t ↔ s ⊆ t ∧ card s = n := sorry

@[simp] theorem powerset_len_mono {α : Type u_1} {n : ℕ} {s : finset α} {t : finset α} (h : s ⊆ t) : powerset_len n s ⊆ powerset_len n t :=
  fun (u : finset α) (h' : u ∈ powerset_len n s) =>
    iff.mpr mem_powerset_len (and.imp (fun (h₂ : u ⊆ s) => subset.trans h₂ h) id (iff.mp mem_powerset_len h'))

@[simp] theorem card_powerset_len {α : Type u_1} (n : ℕ) (s : finset α) : card (powerset_len n s) = nat.choose (card s) n :=
  Eq.trans (multiset.card_pmap mk (multiset.powerset_len n (val s)) (powerset_len._proof_1 n s))
    (multiset.card_powerset_len n (val s))

@[simp] theorem powerset_len_zero {α : Type u_1} (s : finset α) : powerset_len 0 s = singleton ∅ := sorry

theorem powerset_len_eq_filter {α : Type u_1} {n : ℕ} {s : finset α} : powerset_len n s = filter (fun (x : finset α) => card x = n) (powerset s) := sorry


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

! This file was ported from Lean 3 source module set_theory.cardinal.continuum
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.SetTheory.Cardinal.Ordinal

/-!
# Cardinality of continuum

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

In this file we define `cardinal.continuum` (notation: `𝔠`, localized in `cardinal`) to be `2 ^ ℵ₀`.
We also prove some `simp` lemmas about cardinal arithmetic involving `𝔠`.

## Notation

- `𝔠` : notation for `cardinal.continuum` in locale `cardinal`.
-/

namespace Cardinal

universe u v

open Cardinal

#print Cardinal.continuum /-
/-- Cardinality of continuum. -/
def continuum : Cardinal.{u} :=
  2 ^ aleph0.{u}
#align cardinal.continuum Cardinal.continuum
-/

-- mathport name: cardinal.continuum
scoped notation "𝔠" => Cardinal.continuum

/- warning: cardinal.two_power_aleph_0 -> Cardinal.two_power_aleph0 is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHPow.{succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.hasPow.{u1}) (OfNat.ofNat.{succ u1} Cardinal.{u1} 2 (OfNat.mk.{succ u1} Cardinal.{u1} 2 (bit0.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1} (One.one.{succ u1} Cardinal.{u1} Cardinal.hasOne.{u1})))) Cardinal.aleph0.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHPow.{succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.instPowCardinal.{u1}) (OfNat.ofNat.{succ u1} Cardinal.{u1} 2 (instOfNat.{succ u1} Cardinal.{u1} 2 Cardinal.instNatCastCardinal.{u1} (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) Cardinal.aleph0.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.two_power_aleph_0 Cardinal.two_power_aleph0ₓ'. -/
@[simp]
theorem two_power_aleph0 : 2 ^ aleph0.{u} = continuum.{u} :=
  rfl
#align cardinal.two_power_aleph_0 Cardinal.two_power_aleph0

/- warning: cardinal.lift_continuum -> Cardinal.lift_continuum is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ (max u2 u1))} Cardinal.{max u2 u1} (Cardinal.lift.{u1, u2} Cardinal.continuum.{u2}) Cardinal.continuum.{max u2 u1}
but is expected to have type
  Eq.{max (succ (succ u2)) (succ (succ u1))} Cardinal.{max u1 u2} (Cardinal.lift.{u2, u1} Cardinal.continuum.{u1}) Cardinal.continuum.{max u2 u1}
Case conversion may be inaccurate. Consider using '#align cardinal.lift_continuum Cardinal.lift_continuumₓ'. -/
@[simp]
theorem lift_continuum : lift.{v} 𝔠 = 𝔠 := by
  rw [← two_power_aleph_0, lift_two_power, lift_aleph_0, two_power_aleph_0]
#align cardinal.lift_continuum Cardinal.lift_continuum

/-!
### Inequalities
-/

#print Cardinal.aleph0_lt_continuum /-
theorem aleph0_lt_continuum : ℵ₀ < 𝔠 :=
  cantor ℵ₀
#align cardinal.aleph_0_lt_continuum Cardinal.aleph0_lt_continuum
-/

#print Cardinal.aleph0_le_continuum /-
theorem aleph0_le_continuum : ℵ₀ ≤ 𝔠 :=
  aleph0_lt_continuum.le
#align cardinal.aleph_0_le_continuum Cardinal.aleph0_le_continuum
-/

/- warning: cardinal.beth_one -> Cardinal.beth_one is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (Cardinal.beth.{u1} (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (Cardinal.beth.{u1} (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.beth_one Cardinal.beth_oneₓ'. -/
@[simp]
theorem beth_one : beth 1 = 𝔠 := by simpa using beth_succ 0
#align cardinal.beth_one Cardinal.beth_one

/- warning: cardinal.nat_lt_continuum -> Cardinal.nat_lt_continuum is a dubious translation:
lean 3 declaration is
  forall (n : Nat), LT.lt.{succ u1} Cardinal.{u1} (Preorder.toLT.{succ u1} Cardinal.{u1} (PartialOrder.toPreorder.{succ u1} Cardinal.{u1} Cardinal.partialOrder.{u1})) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n) Cardinal.continuum.{u1}
but is expected to have type
  forall (n : Nat), LT.lt.{succ u1} Cardinal.{u1} (Preorder.toLT.{succ u1} Cardinal.{u1} (PartialOrder.toPreorder.{succ u1} Cardinal.{u1} Cardinal.partialOrder.{u1})) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.nat_lt_continuum Cardinal.nat_lt_continuumₓ'. -/
theorem nat_lt_continuum (n : ℕ) : ↑n < 𝔠 :=
  (nat_lt_aleph0 n).trans aleph0_lt_continuum
#align cardinal.nat_lt_continuum Cardinal.nat_lt_continuum

#print Cardinal.mk_set_nat /-
theorem mk_set_nat : (#Set ℕ) = 𝔠 := by simp
#align cardinal.mk_set_nat Cardinal.mk_set_nat
-/

#print Cardinal.continuum_pos /-
theorem continuum_pos : 0 < 𝔠 :=
  nat_lt_continuum 0
#align cardinal.continuum_pos Cardinal.continuum_pos
-/

#print Cardinal.continuum_ne_zero /-
theorem continuum_ne_zero : 𝔠 ≠ 0 :=
  continuum_pos.ne'
#align cardinal.continuum_ne_zero Cardinal.continuum_ne_zero
-/

/- warning: cardinal.aleph_one_le_continuum -> Cardinal.aleph_one_le_continuum is a dubious translation:
lean 3 declaration is
  LE.le.{succ u1} Cardinal.{u1} Cardinal.hasLe.{u1} (Cardinal.aleph.{u1} (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (OfNat.mk.{succ u1} Ordinal.{u1} 1 (One.one.{succ u1} Ordinal.{u1} Ordinal.hasOne.{u1})))) Cardinal.continuum.{u1}
but is expected to have type
  LE.le.{succ u1} Cardinal.{u1} Cardinal.instLECardinal.{u1} (Cardinal.aleph.{u1} (OfNat.ofNat.{succ u1} Ordinal.{u1} 1 (One.toOfNat1.{succ u1} Ordinal.{u1} Ordinal.one.{u1}))) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.aleph_one_le_continuum Cardinal.aleph_one_le_continuumₓ'. -/
theorem aleph_one_le_continuum : aleph 1 ≤ 𝔠 :=
  by
  rw [← succ_aleph_0]
  exact Order.succ_le_of_lt aleph_0_lt_continuum
#align cardinal.aleph_one_le_continuum Cardinal.aleph_one_le_continuum

#print Cardinal.continuum_toNat /-
@[simp]
theorem continuum_toNat : continuum.toNat = 0 :=
  toNat_apply_of_aleph0_le aleph0_le_continuum
#align cardinal.continuum_to_nat Cardinal.continuum_toNat
-/

/- warning: cardinal.continuum_to_part_enat -> Cardinal.continuum_toPartENat is a dubious translation:
lean 3 declaration is
  Eq.{1} PartENat (coeFn.{succ (succ u1), succ (succ u1)} (AddMonoidHom.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.canonicallyOrderedCommSemiring.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.addCommMonoidWithOne)))) (fun (_x : AddMonoidHom.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.canonicallyOrderedCommSemiring.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.addCommMonoidWithOne)))) => Cardinal.{u1} -> PartENat) (AddMonoidHom.hasCoeToFun.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.canonicallyOrderedCommSemiring.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.addCommMonoidWithOne)))) Cardinal.toPartENat.{u1} Cardinal.continuum.{u1}) (Top.top.{0} PartENat PartENat.hasTop)
but is expected to have type
  Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Cardinal.{u1}) => PartENat) Cardinal.continuum.{u1}) (FunLike.coe.{succ (succ u1), succ (succ u1), 1} (AddMonoidHom.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat)))) Cardinal.{u1} (fun (_x : Cardinal.{u1}) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Cardinal.{u1}) => PartENat) _x) (AddHomClass.toFunLike.{succ u1, succ u1, 0} (AddMonoidHom.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat)))) Cardinal.{u1} PartENat (AddZeroClass.toAdd.{succ u1} Cardinal.{u1} (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1}))))))))) (AddZeroClass.toAdd.{0} PartENat (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat)))) (AddMonoidHomClass.toAddHomClass.{succ u1, succ u1, 0} (AddMonoidHom.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat)))) Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat))) (AddMonoidHom.addMonoidHomClass.{succ u1, 0} Cardinal.{u1} PartENat (AddMonoid.toAddZeroClass.{succ u1} Cardinal.{u1} (AddMonoidWithOne.toAddMonoid.{succ u1} Cardinal.{u1} (AddCommMonoidWithOne.toAddMonoidWithOne.{succ u1} Cardinal.{u1} (NonAssocSemiring.toAddCommMonoidWithOne.{succ u1} Cardinal.{u1} (Semiring.toNonAssocSemiring.{succ u1} Cardinal.{u1} (OrderedSemiring.toSemiring.{succ u1} Cardinal.{u1} (OrderedCommSemiring.toOrderedSemiring.{succ u1} Cardinal.{u1} (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{succ u1} Cardinal.{u1} Cardinal.instCanonicallyOrderedCommSemiringCardinal.{u1})))))))) (AddMonoid.toAddZeroClass.{0} PartENat (AddMonoidWithOne.toAddMonoid.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat)))))) Cardinal.toPartENat.{u1} Cardinal.continuum.{u1}) (Top.top.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Cardinal.{u1}) => PartENat) Cardinal.continuum.{u1}) PartENat.instTopPartENat)
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_to_part_enat Cardinal.continuum_toPartENatₓ'. -/
@[simp]
theorem continuum_toPartENat : continuum.toPartENat = ⊤ :=
  toPartENat_apply_of_aleph0_le aleph0_le_continuum
#align cardinal.continuum_to_part_enat Cardinal.continuum_toPartENat

/-!
### Addition
-/

/- warning: cardinal.aleph_0_add_continuum -> Cardinal.aleph0_add_continuum is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1}) Cardinal.aleph0.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.instAddCardinal.{u1}) Cardinal.aleph0.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.aleph_0_add_continuum Cardinal.aleph0_add_continuumₓ'. -/
@[simp]
theorem aleph0_add_continuum : ℵ₀ + 𝔠 = 𝔠 :=
  add_eq_right aleph0_le_continuum aleph0_le_continuum
#align cardinal.aleph_0_add_continuum Cardinal.aleph0_add_continuum

/- warning: cardinal.continuum_add_aleph_0 -> Cardinal.continuum_add_aleph0 is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1}) Cardinal.continuum.{u1} Cardinal.aleph0.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.instAddCardinal.{u1}) Cardinal.continuum.{u1} Cardinal.aleph0.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_add_aleph_0 Cardinal.continuum_add_aleph0ₓ'. -/
@[simp]
theorem continuum_add_aleph0 : 𝔠 + ℵ₀ = 𝔠 :=
  (add_comm _ _).trans aleph0_add_continuum
#align cardinal.continuum_add_aleph_0 Cardinal.continuum_add_aleph0

/- warning: cardinal.continuum_add_self -> Cardinal.continuum_add_self is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1}) Cardinal.continuum.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.instAddCardinal.{u1}) Cardinal.continuum.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_add_self Cardinal.continuum_add_selfₓ'. -/
@[simp]
theorem continuum_add_self : 𝔠 + 𝔠 = 𝔠 :=
  add_eq_right aleph0_le_continuum le_rfl
#align cardinal.continuum_add_self Cardinal.continuum_add_self

/- warning: cardinal.nat_add_continuum -> Cardinal.nat_add_continuum is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1}) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n) Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  forall (n : Nat), Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.instAddCardinal.{u1}) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n) Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.nat_add_continuum Cardinal.nat_add_continuumₓ'. -/
@[simp]
theorem nat_add_continuum (n : ℕ) : ↑n + 𝔠 = 𝔠 :=
  add_eq_right aleph0_le_continuum (nat_lt_continuum n).le
#align cardinal.nat_add_continuum Cardinal.nat_add_continuum

/- warning: cardinal.continuum_add_nat -> Cardinal.continuum_add_nat is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.hasAdd.{u1}) Cardinal.continuum.{u1} ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n)) Cardinal.continuum.{u1}
but is expected to have type
  forall (n : Nat), Eq.{succ (succ u1)} Cardinal.{u1} (HAdd.hAdd.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHAdd.{succ u1} Cardinal.{u1} Cardinal.instAddCardinal.{u1}) Cardinal.continuum.{u1} (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n)) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_add_nat Cardinal.continuum_add_natₓ'. -/
@[simp]
theorem continuum_add_nat (n : ℕ) : 𝔠 + n = 𝔠 :=
  (add_comm _ _).trans (nat_add_continuum n)
#align cardinal.continuum_add_nat Cardinal.continuum_add_nat

/-!
### Multiplication
-/

/- warning: cardinal.continuum_mul_self -> Cardinal.continuum_mul_self is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.hasMul.{u1}) Cardinal.continuum.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.instMulCardinal.{u1}) Cardinal.continuum.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_mul_self Cardinal.continuum_mul_selfₓ'. -/
@[simp]
theorem continuum_mul_self : 𝔠 * 𝔠 = 𝔠 :=
  mul_eq_left aleph0_le_continuum le_rfl continuum_ne_zero
#align cardinal.continuum_mul_self Cardinal.continuum_mul_self

/- warning: cardinal.continuum_mul_aleph_0 -> Cardinal.continuum_mul_aleph0 is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.hasMul.{u1}) Cardinal.continuum.{u1} Cardinal.aleph0.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.instMulCardinal.{u1}) Cardinal.continuum.{u1} Cardinal.aleph0.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_mul_aleph_0 Cardinal.continuum_mul_aleph0ₓ'. -/
@[simp]
theorem continuum_mul_aleph0 : 𝔠 * ℵ₀ = 𝔠 :=
  mul_eq_left aleph0_le_continuum aleph0_le_continuum aleph0_ne_zero
#align cardinal.continuum_mul_aleph_0 Cardinal.continuum_mul_aleph0

/- warning: cardinal.aleph_0_mul_continuum -> Cardinal.aleph0_mul_continuum is a dubious translation:
lean 3 declaration is
  Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.hasMul.{u1}) Cardinal.aleph0.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
but is expected to have type
  Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.instMulCardinal.{u1}) Cardinal.aleph0.{u1} Cardinal.continuum.{u1}) Cardinal.continuum.{u1}
Case conversion may be inaccurate. Consider using '#align cardinal.aleph_0_mul_continuum Cardinal.aleph0_mul_continuumₓ'. -/
@[simp]
theorem aleph0_mul_continuum : ℵ₀ * 𝔠 = 𝔠 :=
  (mul_comm _ _).trans continuum_mul_aleph0
#align cardinal.aleph_0_mul_continuum Cardinal.aleph0_mul_continuum

/- warning: cardinal.nat_mul_continuum -> Cardinal.nat_mul_continuum is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.hasMul.{u1}) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n) Cardinal.continuum.{u1}) Cardinal.continuum.{u1})
but is expected to have type
  forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.instMulCardinal.{u1}) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n) Cardinal.continuum.{u1}) Cardinal.continuum.{u1})
Case conversion may be inaccurate. Consider using '#align cardinal.nat_mul_continuum Cardinal.nat_mul_continuumₓ'. -/
@[simp]
theorem nat_mul_continuum {n : ℕ} (hn : n ≠ 0) : ↑n * 𝔠 = 𝔠 :=
  mul_eq_right aleph0_le_continuum (nat_lt_continuum n).le (Nat.cast_ne_zero.2 hn)
#align cardinal.nat_mul_continuum Cardinal.nat_mul_continuum

/- warning: cardinal.continuum_mul_nat -> Cardinal.continuum_mul_nat is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.hasMul.{u1}) Cardinal.continuum.{u1} ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n)) Cardinal.continuum.{u1})
but is expected to have type
  forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{succ (succ u1)} Cardinal.{u1} (HMul.hMul.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHMul.{succ u1} Cardinal.{u1} Cardinal.instMulCardinal.{u1}) Cardinal.continuum.{u1} (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n)) Cardinal.continuum.{u1})
Case conversion may be inaccurate. Consider using '#align cardinal.continuum_mul_nat Cardinal.continuum_mul_natₓ'. -/
@[simp]
theorem continuum_mul_nat {n : ℕ} (hn : n ≠ 0) : 𝔠 * n = 𝔠 :=
  (mul_comm _ _).trans (nat_mul_continuum hn)
#align cardinal.continuum_mul_nat Cardinal.continuum_mul_nat

/-!
### Power
-/

#print Cardinal.aleph0_power_aleph0 /-
@[simp]
theorem aleph0_power_aleph0 : aleph0.{u} ^ aleph0.{u} = 𝔠 :=
  power_self_eq le_rfl
#align cardinal.aleph_0_power_aleph_0 Cardinal.aleph0_power_aleph0
-/

/- warning: cardinal.nat_power_aleph_0 -> Cardinal.nat_power_aleph0 is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, (LE.le.{0} Nat Nat.hasLe (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))) n) -> (Eq.{succ (succ u1)} Cardinal.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHPow.{succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.hasPow.{u1}) ((fun (a : Type) (b : Type.{succ u1}) [self : HasLiftT.{1, succ (succ u1)} a b] => self.0) Nat Cardinal.{u1} (HasLiftT.mk.{1, succ (succ u1)} Nat Cardinal.{u1} (CoeTCₓ.coe.{1, succ (succ u1)} Nat Cardinal.{u1} (Nat.castCoe.{succ u1} Cardinal.{u1} Cardinal.hasNatCast.{u1}))) n) Cardinal.aleph0.{u1}) Cardinal.continuum.{u1})
but is expected to have type
  forall {n : Nat}, (LE.le.{0} Nat instLENat (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)) n) -> (Eq.{succ (succ u1)} Cardinal.{u1} (HPow.hPow.{succ u1, succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.{u1} (instHPow.{succ u1, succ u1} Cardinal.{u1} Cardinal.{u1} Cardinal.instPowCardinal.{u1}) (Nat.cast.{succ u1} Cardinal.{u1} Cardinal.instNatCastCardinal.{u1} n) Cardinal.aleph0.{u1}) Cardinal.continuum.{u1})
Case conversion may be inaccurate. Consider using '#align cardinal.nat_power_aleph_0 Cardinal.nat_power_aleph0ₓ'. -/
@[simp]
theorem nat_power_aleph0 {n : ℕ} (hn : 2 ≤ n) : (n ^ aleph0.{u} : Cardinal.{u}) = 𝔠 :=
  nat_power_eq le_rfl hn
#align cardinal.nat_power_aleph_0 Cardinal.nat_power_aleph0

#print Cardinal.continuum_power_aleph0 /-
@[simp]
theorem continuum_power_aleph0 : continuum.{u} ^ aleph0.{u} = 𝔠 := by
  rw [← two_power_aleph_0, ← power_mul, mul_eq_left le_rfl le_rfl aleph_0_ne_zero]
#align cardinal.continuum_power_aleph_0 Cardinal.continuum_power_aleph0
-/

end Cardinal


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

! This file was ported from Lean 3 source module category_theory.functor.left_derived
! leanprover-community/mathlib commit 13ff898b0eee75d3cc75d1c06a491720eaaf911d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Preadditive.ProjectiveResolution

/-!
# Left-derived functors

We define the left-derived functors `F.left_derived n : C ⥤ D` for any additive functor `F`
out of a category with projective resolutions.

The definition is
```
projective_resolutions C ⋙ F.map_homotopy_category _ ⋙ homotopy_category.homology_functor D _ n
```
that is, we pick a projective resolution (thought of as an object of the homotopy category),
we apply `F` objectwise, and compute `n`-th homology.

We show that these left-derived functors can be calculated
on objects using any choice of projective resolution,
and on morphisms by any choice of lift to a chain map between chosen projective resolutions.

Similarly we define natural transformations between left-derived functors coming from
natural transformations between the original additive functors,
and show how to compute the components.

## Implementation

We don't assume the categories involved are abelian
(just preadditive, and have equalizers, cokernels, and image maps),
or that the functors are right exact.
None of these assumptions are needed yet.

It is often convenient, of course, to work with `[abelian C] [enough_projectives C] [abelian D]`
which (assuming the results from `category_theory.abelian.projective`) are enough to
provide all the typeclass hypotheses assumed here.
-/

noncomputable section

open CategoryTheory

open CategoryTheory.Limits

universe v u

namespace CategoryTheory

variable {C : Type u} [Category.{v} C] {D : Type _} [Category D]

-- Importing `category_theory.abelian.projective` and assuming
-- `[abelian C] [enough_projectives C] [abelian D]` suffices to acquire all the following:
variable [Preadditive C] [HasZeroObject C] [HasEqualizers C] [HasImages C]
  [HasProjectiveResolutions C]

variable [Preadditive D] [HasEqualizers D] [HasCokernels D] [HasImages D] [HasImageMaps D]

/-- The left derived functors of an additive functor. -/
def Functor.leftDerived (F : C ⥤ D) [F.Additive] (n : ℕ) : C ⥤ D :=
  projectiveResolutions C ⋙ F.mapHomotopyCategory _ ⋙ HomotopyCategory.homologyFunctor D _ n
#align category_theory.functor.left_derived CategoryTheory.Functor.leftDerived

-- TODO the left derived functors are additive (and linear when `F` is linear)
/-- We can compute a left derived functor using a chosen projective resolution. -/
@[simps]
def Functor.leftDerivedObjIso (F : C ⥤ D) [F.Additive] (n : ℕ) {X : C}
    (P : ProjectiveResolution X) :
    (F.leftDerived n).obj X ≅
      (homologyFunctor D _ n).obj ((F.mapHomologicalComplex _).obj P.complex) :=
  (HomotopyCategory.homologyFunctor D _ n).mapIso
      (HomotopyCategory.isoOfHomotopyEquiv
        (F.mapHomotopyEquiv (ProjectiveResolution.homotopyEquiv _ P))) ≪≫
    (HomotopyCategory.homologyFactors D _ n).app _
#align category_theory.functor.left_derived_obj_iso CategoryTheory.Functor.leftDerivedObjIso

section

variable [HasZeroObject D]

/-- The 0-th derived functor of `F` on a projective object `X` is just `F.obj X`. -/
@[simps]
def Functor.leftDerivedObjProjectiveZero (F : C ⥤ D) [F.Additive] (X : C) [Projective X] :
    (F.leftDerived 0).obj X ≅ F.obj X :=
  F.leftDerivedObjIso 0 (ProjectiveResolution.self X) ≪≫
    (homologyFunctor _ _ _).mapIso ((ChainComplex.single₀MapHomologicalComplex F).app X) ≪≫
      (ChainComplex.homologyFunctor0Single₀ D).app (F.obj X)
#align category_theory.functor.left_derived_obj_projective_zero CategoryTheory.Functor.leftDerivedObjProjectiveZero

open ZeroObject

/-- The higher derived functors vanish on projective objects. -/
@[simps inv]
def Functor.leftDerivedObjProjectiveSucc (F : C ⥤ D) [F.Additive] (n : ℕ) (X : C) [Projective X] :
    (F.leftDerived (n + 1)).obj X ≅ 0 :=
  F.leftDerivedObjIso (n + 1) (ProjectiveResolution.self X) ≪≫
    (homologyFunctor _ _ _).mapIso ((ChainComplex.single₀MapHomologicalComplex F).app X) ≪≫
      (ChainComplex.homologyFunctorSuccSingle₀ D n).app (F.obj X) ≪≫ (Functor.zero_obj _).isoZero
#align category_theory.functor.left_derived_obj_projective_succ CategoryTheory.Functor.leftDerivedObjProjectiveSucc

end

/-- We can compute a left derived functor on a morphism using a lift of that morphism
to a chain map between chosen projective resolutions.
-/
theorem Functor.leftDerived_map_eq (F : C ⥤ D) [F.Additive] (n : ℕ) {X Y : C} (f : X ⟶ Y)
    {P : ProjectiveResolution X} {Q : ProjectiveResolution Y} (g : P.complex ⟶ Q.complex)
    (w : g ≫ Q.π = P.π ≫ (ChainComplex.single₀ C).map f) :
    (F.leftDerived n).map f =
      (F.leftDerivedObjIso n P).Hom ≫
        (homologyFunctor D _ n).map ((F.mapHomologicalComplex _).map g) ≫
          (F.leftDerivedObjIso n Q).inv :=
  by
  dsimp only [functor.left_derived, functor.left_derived_obj_iso]
  dsimp; simp only [category.comp_id, category.id_comp]
  rw [← homologyFunctor_map, HomotopyCategory.homologyFunctor_map_factors]
  simp only [← functor.map_comp]
  congr 1
  apply HomotopyCategory.eq_of_homotopy
  apply functor.map_homotopy
  apply Homotopy.trans
  exact HomotopyCategory.homotopyOutMap _
  apply ProjectiveResolution.lift_homotopy f
  · simp
  · simp [w]
#align category_theory.functor.left_derived_map_eq CategoryTheory.Functor.leftDerived_map_eq

/-- The natural transformation between left-derived functors induced by a natural transformation. -/
@[simps]
def NatTrans.leftDerived {F G : C ⥤ D} [F.Additive] [G.Additive] (α : F ⟶ G) (n : ℕ) :
    F.leftDerived n ⟶ G.leftDerived n :=
  whiskerLeft (projectiveResolutions C)
    (whiskerRight (NatTrans.mapHomotopyCategory α _) (HomotopyCategory.homologyFunctor D _ n))
#align category_theory.nat_trans.left_derived CategoryTheory.NatTrans.leftDerived

@[simp]
theorem NatTrans.leftDerived_id (F : C ⥤ D) [F.Additive] (n : ℕ) :
    NatTrans.leftDerived (𝟙 F) n = 𝟙 (F.leftDerived n) :=
  by
  simp [nat_trans.left_derived]
  rfl
#align category_theory.nat_trans.left_derived_id CategoryTheory.NatTrans.leftDerived_id

-- The `simp_nf` linter times out here, so we disable it.
@[simp, nolint simp_nf]
theorem NatTrans.leftDerived_comp {F G H : C ⥤ D} [F.Additive] [G.Additive] [H.Additive] (α : F ⟶ G)
    (β : G ⟶ H) (n : ℕ) :
    NatTrans.leftDerived (α ≫ β) n = NatTrans.leftDerived α n ≫ NatTrans.leftDerived β n := by
  simp [nat_trans.left_derived]
#align category_theory.nat_trans.left_derived_comp CategoryTheory.NatTrans.leftDerived_comp

/-- A component of the natural transformation between left-derived functors can be computed
using a chosen projective resolution.
-/
theorem NatTrans.leftDerived_eq {F G : C ⥤ D} [F.Additive] [G.Additive] (α : F ⟶ G) (n : ℕ) {X : C}
    (P : ProjectiveResolution X) :
    (NatTrans.leftDerived α n).app X =
      (F.leftDerivedObjIso n P).Hom ≫
        (homologyFunctor D _ n).map ((NatTrans.mapHomologicalComplex α _).app P.complex) ≫
          (G.leftDerivedObjIso n P).inv :=
  by
  symm
  dsimp [nat_trans.left_derived, functor.left_derived_obj_iso]
  simp only [category.comp_id, category.id_comp]
  rw [← homologyFunctor_map, HomotopyCategory.homologyFunctor_map_factors]
  simp only [← functor.map_comp]
  congr 1
  apply HomotopyCategory.eq_of_homotopy
  simp only [nat_trans.map_homological_complex_naturality_assoc, ← functor.map_comp]
  apply Homotopy.compLeftId
  rw [← Functor.map_id]
  apply functor.map_homotopy
  apply HomotopyEquiv.homotopyHomInvId
#align category_theory.nat_trans.left_derived_eq CategoryTheory.NatTrans.leftDerived_eq

-- TODO:
-- lemma nat_trans.left_derived_projective_zero {F G : C ⥤ D} [F.additive] [G.additive] (α : F ⟶ G)
--   (X : C) [projective X] :
--   (nat_trans.left_derived α 0).app X =
--     (F.left_derived_obj_projective_zero X).hom ≫
--       α.app X ≫
--         (G.left_derived_obj_projective_zero X).inv := sorry
-- TODO:
-- lemma nat_trans.left_derived_projective_succ {F G : C ⥤ D} [F.additive] [G.additive] (α : F ⟶ G)
--   (n : ℕ) (X : C) [projective X] :
--   (nat_trans.left_derived α (n+1)).app X = 0 := sorry
-- TODO left-derived functors of the identity functor are the identity
-- (requires we assume `abelian`?)
-- PROJECT left-derived functors of a composition (Grothendieck sequence)
end CategoryTheory


import .basic .induction .single order.basic
----------------------------------------------------------------
open_locale classical
noncomputable theory

namespace ftype
-- The operations needed on the ftype A.
section minmax

variables {U : ftype} {α : Type u} [linear_order α]

-- Proving this stuff probably needs fintype etc for ftype.

/-- finds the argmin of f over all sets X satisfying P -/
lemma exists_arg_max_over_subtype (P : set U → Prop) (hP : set.nonempty P) (f : { X : set U // P X } → α) :
  ∃ X, ∀ Y, f Y ≤ f X :=
begin
  letI := fintype_of U,
  haveI : nonempty { X : set U // P X } := ⟨classical.subtype_of_exists hP⟩,
  from fintype.exists_max f,
end

/-- finds the argmin of f over all sets X satisfying P -/
lemma exists_arg_min_over_subtype (P : set U → Prop) (hP : set.nonempty P) (f : { X : set U // P X } → α) :
  ∃ X, ∀ Y, f X ≤ f Y :=
let f' : _ → (order_dual α) := f in exists_arg_max_over_subtype P hP f'

/-- maximum value of f over subsets of U satisfying P -/
def arg_max_over (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) : set U :=
  (classical.some (exists_arg_max_over_subtype P hP (λ X, f X.val))).val

/-- subset of U attaining maximum value of f subject to satisfying P -/
def max_val_over (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) : α :=
  f (arg_max_over P hP f)

lemma max_over_is_ub (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) :
  ∀ X, P X → f X ≤ max_val_over P hP f :=
λ X hX, classical.some_spec (exists_arg_max_over_subtype P hP (λ X, f X.val)) ⟨X, hX⟩

lemma arg_max_over_attains (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) :
  P (arg_max_over P hP f) ∧ f (arg_max_over P hP f) = max_val_over P hP f :=
let X' := (classical.some (exists_arg_max_over_subtype P hP (λ X, f X.val))) in
⟨X'.property, rfl⟩

lemma max_over_spec (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) :
  ∃ X, P X ∧ (f X = max_val_over P hP f) ∧ (∀ Y, P Y → f Y ≤ f X)  :=
let prev := arg_max_over_attains P hP f in
⟨arg_max_over P hP f,⟨prev.1,prev.2,max_over_is_ub P hP f⟩⟩

/-- minimum value of f over subsets of U satisfying P -/
def arg_min_over (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) : set U :=
  (classical.some (exists_arg_min_over_subtype P hP (λ X, f X.val))).val

/-- subset of U attaining minimum value of f subject to satisfying P -/
def min_val_over (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) : α :=
  f (arg_min_over P hP f)

lemma min_over_is_lb (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) :
  ∀ X, P X → min_val_over P hP f ≤ f X :=
λ X hX, classical.some_spec (exists_arg_min_over_subtype P hP (λ X, f X.val)) ⟨X, hX⟩

lemma arg_min_over_attains (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) :
  P (arg_min_over P hP f) ∧ f (arg_min_over P hP f) = min_val_over P hP f :=
let X' := (classical.some (exists_arg_min_over_subtype P hP (λ X, f X.val))) in
⟨X'.property, rfl⟩

lemma min_over_spec (P : set U → Prop) (hP : set.nonempty P) (f : set U → α) :
  ∃ X, P X ∧ (f X = min_val_over P hP f) ∧ (∀ Y, P Y → f X ≤ f Y)   :=
let prev := arg_min_over_attains P hP f in
⟨arg_min_over P hP f,⟨prev.1,prev.2,min_over_is_lb P hP f⟩⟩
----

/-- maximum value of f over subsets of U -/
def max_val (f : set U → α) : α := max_val_over (λ X, true) ⟨∅, trivial⟩ f

/-- subset of U attaining maximum value of f -/
def arg_max (f : set U → α) : set U := arg_max_over (λ X, true) ⟨∅, trivial⟩ f

/-- minimum value of f over subsets of U -/
def min_val (f : set U → α) : α := min_val_over (λ X, true) ⟨∅, trivial⟩ f

/-- subset of U attaining minimum value of f-/
def arg_min (f : set U → α) : set U := arg_min_over (λ X, true) ⟨∅, trivial⟩ f

lemma max_is_ub (f : set U → α) :
  ∀ X, f X ≤ max_val f :=
λ X, by {apply max_over_is_ub, trivial}

lemma arg_max_attains (f : set U → α) :
  f (arg_max f) = max_val f :=
(arg_max_over_attains _ _ _).2

lemma min_is_lb (f : set U → α) :
  ∀ X, min_val f ≤ f X :=
λ X, by {apply min_over_is_lb, trivial}

lemma arg_min_attains (f : set U → α) :
  f (arg_min f) = min_val f :=
(arg_min_over_attains _ _ _).2

lemma min_spec (f : set U → α) :
  ∃ X, (∀ Y, f X ≤ f Y) ∧ (f X = min_val f) :=
⟨arg_min f, ⟨min_is_lb f, arg_min_attains f⟩ ⟩

lemma max_spec (f : set U → α) :
  ∃ X, (∀ Y, f Y ≤ f X) ∧ (f X = max_val f) :=
⟨arg_max f, ⟨max_is_ub f, arg_max_attains f⟩ ⟩

-- size

/-- largest set satisfying P -/
def arg_max_size_over (P : set U → Prop) (hP : set.nonempty P) : set U :=
  arg_max_over P hP size

/-- size of largest set satisfying P -/
def max_size_over (P : set U → Prop) (hP : set.nonempty P) : ℤ :=
  size (arg_max_size_over P hP)

lemma max_size_over_is_ub (P : set U → Prop) (hP : set.nonempty P) :
  ∀ X, P X → size X ≤ max_size_over P hP:=
max_over_is_ub P hP size

lemma arg_max_size_over_attains (P : set U → Prop) (hP : set.nonempty P) :
  P (arg_max_size_over P hP) ∧ size (arg_max_size_over P hP) = max_size_over P hP :=
  arg_max_over_attains P hP size

lemma max_size_over_spec (P : set U → Prop) (hP : set.nonempty P) :
  ∃ X, P X ∧ (size X = max_size_over P hP) ∧ (∀ Y, P Y → size Y ≤ size X)  :=
  max_over_spec P hP size

/-- smallest set satisfying P -/
def arg_min_size_over (P : set U → Prop) (hP : set.nonempty P) : set U :=
  arg_min_over P hP size

/-- size of smallest set satisfying P -/
def min_size_over (P : set U → Prop) (hP : set.nonempty P) : ℤ :=
  size (arg_min_size_over P hP)

lemma min_size_over_is_lb (P : set U → Prop) (hP : set.nonempty P) :
  ∀ X, P X → min_size_over P hP ≤ size X :=
min_over_is_lb P hP size

lemma arg_min_size_over_attains (P : set U → Prop) (hP : set.nonempty P) :
  P (arg_min_size_over P hP) ∧ size (arg_min_size_over P hP) = min_size_over P hP :=
  arg_min_over_attains P hP size

lemma min_size_over_spec (P : set U → Prop) (hP : set.nonempty P) :
  ∃ X, P X ∧ (size X = min_size_over P hP) ∧ (∀ Y, P Y → size X ≤ size Y)  :=
  min_over_spec P hP size

--- Monotonicity

lemma max_over_subset_le_max (P Q : set U → Prop) (hP : set.nonempty P) (f : set U → α) (hPQ : set.subset P Q) :
   max_val_over P hP f ≤ max_val_over Q (set.nonempty.mono hPQ hP) f :=
begin
  rcases max_over_spec P hP f with ⟨XP, hXP⟩,
  rcases max_over_spec Q (set.nonempty.mono hPQ hP) f with ⟨XQ, hXQ⟩,
  rw [←hXP.2.1, ←hXQ.2.1],
  from hXQ.2.2  _ (hPQ hXP.1),
end

lemma min_le_min_over_subset (P Q : set U → Prop) (hP : set.nonempty P) (f : set U → α) (hPQ : set.subset P Q) :
  min_val_over Q (set.nonempty.mono hPQ hP) f ≤ min_val_over P hP f :=
begin
  rcases min_over_spec P hP f with ⟨XP, hXP⟩,
  rcases min_over_spec Q (set.nonempty.mono hPQ hP) f with ⟨XQ, hXQ⟩,
  rw [←hXP.2.1, ←hXQ.2.1],
  from hXQ.2.2  _ (hPQ hXP.1),
end

lemma max_over_le_max_over_of_mono (P : set U → Prop) (hP : set.nonempty P) (f f' : set U → α) (hff' : ∀ X, f X ≤ f' X) :
  max_val_over P hP f ≤ max_val_over P hP f' :=
begin
  rcases max_over_spec P hP f with ⟨X,hX⟩,
  rcases max_over_spec P hP f' with ⟨X', hX'⟩,
  rw [←hX.2.1, ←hX'.2.1],
  from le_trans (hff' X) (hX'.2.2 _ hX.1),
end

lemma min_over_le_min_over_of_mono (P : set U → Prop) (hP : set.nonempty P) (f f' : set U → α) (hff' : ∀ X, f X ≤ f' X) :
  min_val_over P hP f ≤ min_val_over P hP f' :=
begin
  rcases min_over_spec P hP f with ⟨X,hX⟩,
  rcases min_over_spec P hP f' with ⟨X', hX'⟩,
  rw [←hX.2.1, ←hX'.2.1],
  from le_trans (hX.2.2 X' hX'.1) (hff' _),
end

variables {β : Type} [linear_order β]

lemma max_over_compose_mono (P : set U → Prop) (hP : set.nonempty P)
  (f : set U → α) (g : α → β) (hg : monotone g) :
  g (max_val_over P hP f) = max_val_over P hP (g ∘ f) :=
begin
  rcases max_over_spec P hP f with ⟨X, hX₁, hX₂, hX₃⟩,
  rcases max_over_spec P hP (g ∘ f) with ⟨X',hX'₁, hX'₂, hX'₃ ⟩,
  erw [←hX'₂ , ←hX₂],
  from le_antisymm (hX'₃ _ hX₁) (hg (hX₃ _ hX'₁)),
end

lemma min_over_compose_mono (P : set U → Prop) (hP : set.nonempty P)
  (f : set U → α) (g : α → β) (hg : monotone g) :
  g (min_val_over P hP f) = min_val_over P hP (g ∘ f) :=
begin
  rcases min_over_spec P hP f with ⟨X, hX₁, hX₂, hX₃⟩,
  rcases min_over_spec P hP (g ∘ f) with ⟨X',hX'₁, hX'₂, hX'₃ ⟩,
  erw [←hX'₂, ←hX₂],
  from le_antisymm (hg (hX₃ _ hX'₁)) (hX'₃ _ hX₁),
end

/-
lemma max_over_rw_prop (P Q : set U → Prop) (hP : set.nonempty P) (hPQ : P = Q) (f : set U → α) :
  max_val_over P hP f = max_val_over Q (by {rw ←hPQ, from hP}) f :=
by congr'

lemma min_over_rw_prop (P Q : set U → Prop) (hP : set.nonempty P) (hPQ : P = Q) (f : set U → α) :
  min_val_over P hP f = min_val_over Q (by {rw ←hPQ, from hP}) f :=
by congr'
-/

end minmax

end ftype


/-
Copyright (c) 2022 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import data.finset.locally_finite
import data.dfinsupp.interval
import data.dfinsupp.multiset
import data.nat.interval

/-!
# Finite intervals of multisets

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

This file provides the `locally_finite_order` instance for `multiset α` and calculates the
cardinality of its finite intervals.

## Implementation notes

We implement the intervals via the intervals on `dfinsupp`, rather than via filtering
`multiset.powerset`; this is because `(multiset.replicate n x).powerset` has `2^n` entries not `n+1`
entries as it contains duplicates. We do not go via `finsupp` as this would be noncomputable, and
multisets are typically used computationally.

-/

open finset dfinsupp function
open_locale big_operators pointwise

variables {α : Type*} {β : α → Type*}

namespace multiset

variables [decidable_eq α] (f g : multiset α)

instance : locally_finite_order (multiset α) :=
locally_finite_order.of_Icc (multiset α)
  (λ f g, (finset.Icc f.to_dfinsupp g.to_dfinsupp).map
    (multiset.equiv_dfinsupp.to_equiv.symm.to_embedding))
  (λ f g x, by simp)

lemma Icc_eq :
  finset.Icc f g =
    (finset.Icc f.to_dfinsupp g.to_dfinsupp).map
      (multiset.equiv_dfinsupp.to_equiv.symm.to_embedding) := rfl

lemma card_Ico :
  (finset.Ico f g).card = ∏ i in f.to_finset ∪ g.to_finset, (g.count i + 1 - f.count i) - 1 :=
by rw [card_Ico_eq_card_Icc_sub_one, card_Icc]

lemma card_Ioc :
  (finset.Ioc f g).card = ∏ i in f.to_finset ∪ g.to_finset, (g.count i + 1 - f.count i) - 1 :=
by rw [card_Ioc_eq_card_Icc_sub_one, card_Icc]

lemma card_Ioo :
  (finset.Ioo f g).card = ∏ i in f.to_finset ∪ g.to_finset, (g.count i + 1 - f.count i) - 2 :=
by rw [card_Ioo_eq_card_Icc_sub_two, card_Icc]

lemma card_Iic :
  (finset.Iic f).card = ∏ i in f.to_finset, (f.count i + 1) :=
by simp_rw [Iic_eq_Icc, card_Icc, bot_eq_zero, to_finset_zero, empty_union, count_zero, tsub_zero]

end multiset


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

! This file was ported from Lean 3 source module order.galois_connection
! leanprover-community/mathlib commit c5c7e2760814660967bc27f0de95d190a22297f3
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.CompleteLattice
import Mathlib.Order.Synonym
import Mathlib.Order.Hom.Set
import Mathlib.Order.Bounds.Basic

/-!
# Galois connections, insertions and coinsertions

Galois connections are order theoretic adjoints, i.e. a pair of functions `u` and `l`,
such that `∀ a b, l a ≤ b ↔ a ≤ u b`.

## Main definitions

* `GaloisConnection`: A Galois connection is a pair of functions `l` and `u` satisfying
  `l a ≤ b ↔ a ≤ u b`. They are special cases of adjoint functors in category theory,
  but do not depend on the category theory library in mathlib.
* `GaloisInsertion`: A Galois insertion is a Galois connection where `l ∘ u = id`
* `GaloisCoinsertion`: A Galois coinsertion is a Galois connection where `u ∘ l = id`

## Implementation details

Galois insertions can be used to lift order structures from one type to another.
For example if `α` is a complete lattice, and `l : α → β`, and `u : β → α` form a Galois insertion,
then `β` is also a complete lattice. `l` is the lower adjoint and `u` is the upper adjoint.

An example of a Galois insertion is in group theory. If `G` is a group, then there is a Galois
insertion between the set of subsets of `G`, `Set G`, and the set of subgroups of `G`,
`Subgroup G`. The lower adjoint is `Subgroup.closure`, taking the `Subgroup` generated by a `Set`,
and the upper adjoint is the coercion from `Subgroup G` to `Set G`, taking the underlying set
of a subgroup.

Naively lifting a lattice structure along this Galois insertion would mean that the definition
of `inf` on subgroups would be `Subgroup.closure (↑S ∩ ↑T)`. This is an undesirable definition
because the intersection of subgroups is already a subgroup, so there is no need to take the
closure. For this reason a `choice` function is added as a field to the `GaloisInsertion`
structure. It has type `Π S : Set G, ↑(closure S) ≤ S → Subgroup G`. When `↑(closure S) ≤ S`, then
`S` is already a subgroup, so this function can be defined using `Subgroup.mk` and not `closure`.
This means the infimum of subgroups will be defined to be the intersection of sets, paired
with a proof that intersection of subgroups is a subgroup, rather than the closure of the
intersection.
-/

open Function OrderDual Set

universe u v w x

variable {α : Type u} {β : Type v} {γ : Type w} {ι : Sort x} {κ : ι → Sort _} {a a₁ a₂ : α}
  {b b₁ b₂ : β}

/-- A Galois connection is a pair of functions `l` and `u` satisfying
  `l a ≤ b ↔ a ≤ u b`. They are special cases of adjoint functors in category theory,
    but do not depend on the category theory library in mathlib. -/
def GaloisConnection [Preorder α] [Preorder β] (l : α → β) (u : β → α) :=
  ∀ a b, l a ≤ b ↔ a ≤ u b
#align galois_connection GaloisConnection

/-- Makes a Galois connection from an order-preserving bijection. -/
theorem OrderIso.to_galoisConnection [Preorder α] [Preorder β] (oi : α ≃o β) :
    GaloisConnection oi oi.symm := fun _ _ => oi.rel_symm_apply.symm
#align order_iso.to_galois_connection OrderIso.to_galoisConnection

namespace GaloisConnection

section

variable [Preorder α] [Preorder β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem monotone_intro (hu : Monotone u) (hl : Monotone l) (hul : ∀ a, a ≤ u (l a))
    (hlu : ∀ a, l (u a) ≤ a) : GaloisConnection l u := fun _ _ =>
  ⟨fun h => (hul _).trans (hu h), fun h => (hl h).trans (hlu _)⟩
#align galois_connection.monotone_intro GaloisConnection.monotone_intro

protected theorem dual {l : α → β} {u : β → α} (gc : GaloisConnection l u) :
    GaloisConnection (OrderDual.toDual ∘ u ∘ OrderDual.ofDual)
      (OrderDual.toDual ∘ l ∘ OrderDual.ofDual) :=
  fun a b => (gc b a).symm
#align galois_connection.dual GaloisConnection.dual

theorem le_iff_le {a : α} {b : β} : l a ≤ b ↔ a ≤ u b :=
  gc _ _
#align galois_connection.le_iff_le GaloisConnection.le_iff_le

theorem l_le {a : α} {b : β} : a ≤ u b → l a ≤ b :=
  (gc _ _).mpr
#align galois_connection.l_le GaloisConnection.l_le

theorem le_u {a : α} {b : β} : l a ≤ b → a ≤ u b :=
  (gc _ _).mp
#align galois_connection.le_u GaloisConnection.le_u

theorem le_u_l (a) : a ≤ u (l a) :=
  gc.le_u <| le_rfl
#align galois_connection.le_u_l GaloisConnection.le_u_l

theorem l_u_le (a) : l (u a) ≤ a :=
  gc.l_le <| le_rfl
#align galois_connection.l_u_le GaloisConnection.l_u_le

theorem monotone_u : Monotone u := fun a _ H => gc.le_u ((gc.l_u_le a).trans H)
#align galois_connection.monotone_u GaloisConnection.monotone_u

theorem monotone_l : Monotone l :=
  gc.dual.monotone_u.dual
#align galois_connection.monotone_l GaloisConnection.monotone_l

theorem upperBounds_l_image (s : Set α) : upperBounds (l '' s) = u ⁻¹' upperBounds s :=
  Set.ext fun b => by simp [upperBounds, gc _ _]
#align galois_connection.upper_bounds_l_image GaloisConnection.upperBounds_l_image

theorem lowerBounds_u_image (s : Set β) : lowerBounds (u '' s) = l ⁻¹' lowerBounds s :=
  gc.dual.upperBounds_l_image s
#align galois_connection.lower_bounds_u_image GaloisConnection.lowerBounds_u_image

theorem bddAbove_l_image {s : Set α} : BddAbove (l '' s) ↔ BddAbove s :=
  ⟨fun ⟨x, hx⟩ => ⟨u x, by rwa [gc.upperBounds_l_image] at hx⟩, gc.monotone_l.map_bddAbove⟩
#align galois_connection.bdd_above_l_image GaloisConnection.bddAbove_l_image

theorem bddBelow_u_image {s : Set β} : BddBelow (u '' s) ↔ BddBelow s :=
  gc.dual.bddAbove_l_image
#align galois_connection.bdd_below_u_image GaloisConnection.bddBelow_u_image

theorem isLUB_l_image {s : Set α} {a : α} (h : IsLUB s a) : IsLUB (l '' s) (l a) :=
  ⟨gc.monotone_l.mem_upperBounds_image h.left, fun b hb =>
    gc.l_le <| h.right <| by rwa [gc.upperBounds_l_image] at hb⟩
#align galois_connection.is_lub_l_image GaloisConnection.isLUB_l_image

theorem isGLB_u_image {s : Set β} {b : β} (h : IsGLB s b) : IsGLB (u '' s) (u b) :=
  gc.dual.isLUB_l_image h
#align galois_connection.is_glb_u_image GaloisConnection.isGLB_u_image

theorem isLeast_l {a : α} : IsLeast { b | a ≤ u b } (l a) :=
  ⟨gc.le_u_l _, fun _ hb => gc.l_le hb⟩
#align galois_connection.is_least_l GaloisConnection.isLeast_l

theorem isGreatest_u {b : β} : IsGreatest { a | l a ≤ b } (u b) :=
  gc.dual.isLeast_l
#align galois_connection.is_greatest_u GaloisConnection.isGreatest_u

theorem isGLB_l {a : α} : IsGLB { b | a ≤ u b } (l a) :=
  gc.isLeast_l.isGLB
#align galois_connection.is_glb_l GaloisConnection.isGLB_l

theorem isLUB_u {b : β} : IsLUB { a | l a ≤ b } (u b) :=
  gc.isGreatest_u.isLUB
#align galois_connection.is_lub_u GaloisConnection.isLUB_u

/-- If `(l, u)` is a Galois connection, then the relation `x ≤ u (l y)` is a transitive relation.
If `l` is a closure operator (`Submodule.span`, `Subgroup.closure`, ...) and `u` is the coercion to
`Set`, this reads as "if `U` is in the closure of `V` and `V` is in the closure of `W` then `U` is
in the closure of `W`". -/
theorem le_u_l_trans {x y z : α} (hxy : x ≤ u (l y)) (hyz : y ≤ u (l z)) : x ≤ u (l z) :=
  hxy.trans (gc.monotone_u <| gc.l_le hyz)
#align galois_connection.le_u_l_trans GaloisConnection.le_u_l_trans

theorem l_u_le_trans {x y z : β} (hxy : l (u x) ≤ y) (hyz : l (u y) ≤ z) : l (u x) ≤ z :=
  (gc.monotone_l <| gc.le_u hxy).trans hyz
#align galois_connection.l_u_le_trans GaloisConnection.l_u_le_trans

end

section PartialOrder

variable [PartialOrder α] [Preorder β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem u_l_u_eq_u (b : β) : u (l (u b)) = u b :=
  (gc.monotone_u (gc.l_u_le _)).antisymm (gc.le_u_l _)
#align galois_connection.u_l_u_eq_u GaloisConnection.u_l_u_eq_u

theorem u_l_u_eq_u' : u ∘ l ∘ u = u :=
  funext gc.u_l_u_eq_u
#align galois_connection.u_l_u_eq_u' GaloisConnection.u_l_u_eq_u'

theorem u_unique {l' : α → β} {u' : β → α} (gc' : GaloisConnection l' u') (hl : ∀ a, l a = l' a)
    {b : β} : u b = u' b :=
  le_antisymm (gc'.le_u <| hl (u b) ▸ gc.l_u_le _) (gc.le_u <| (hl (u' b)).symm ▸ gc'.l_u_le _)
#align galois_connection.u_unique GaloisConnection.u_unique

/-- If there exists a `b` such that `a = u a`, then `b = l a` is one such element. -/
theorem exists_eq_u (a : α) : (∃ b : β, a = u b) ↔ a = u (l a) :=
  ⟨fun ⟨_, hS⟩ => hS.symm ▸ (gc.u_l_u_eq_u _).symm, fun HI => ⟨_, HI⟩⟩
#align galois_connection.exists_eq_u GaloisConnection.exists_eq_u

theorem u_eq {z : α} {y : β} : u y = z ↔ ∀ x, x ≤ z ↔ l x ≤ y := by
  constructor
  · rintro rfl x
    exact (gc x y).symm
  · intro H
    exact ((H <| u y).mpr (gc.l_u_le y)).antisymm ((gc _ _).mp <| (H z).mp le_rfl)
#align galois_connection.u_eq GaloisConnection.u_eq

end PartialOrder

section PartialOrder

variable [Preorder α] [PartialOrder β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem l_u_l_eq_l (a : α) : l (u (l a)) = l a :=
  (gc.l_u_le _).antisymm (gc.monotone_l (gc.le_u_l _))
#align galois_connection.l_u_l_eq_l GaloisConnection.l_u_l_eq_l

theorem l_u_l_eq_l' : l ∘ u ∘ l = l :=
  funext gc.l_u_l_eq_l
#align galois_connection.l_u_l_eq_l' GaloisConnection.l_u_l_eq_l'

theorem l_unique {l' : α → β} {u' : β → α} (gc' : GaloisConnection l' u') (hu : ∀ b, u b = u' b)
    {a : α} : l a = l' a :=
  le_antisymm (gc.l_le <| (hu (l' a)).symm ▸ gc'.le_u_l _) (gc'.l_le <| hu (l a) ▸ gc.le_u_l _)
#align galois_connection.l_unique GaloisConnection.l_unique

/-- If there exists an `a` such that `b = l a`, then `a = u b` is one such element. -/
theorem exists_eq_l (b : β) : (∃ a : α, b = l a) ↔ b = l (u b) :=
  ⟨fun ⟨_, hS⟩ => hS.symm ▸ (gc.l_u_l_eq_l _).symm, fun HI => ⟨_, HI⟩⟩
#align galois_connection.exists_eq_l GaloisConnection.exists_eq_l

theorem l_eq {x : α} {z : β} : l x = z ↔ ∀ y, z ≤ y ↔ x ≤ u y := by
  constructor
  · rintro rfl y
    exact gc x y
  · intro H
    exact ((gc _ _).mpr <| (H z).mp le_rfl).antisymm ((H <| l x).mpr (gc.le_u_l x))
#align galois_connection.l_eq GaloisConnection.l_eq

end PartialOrder

section OrderTop

variable [PartialOrder α] [Preorder β] [OrderTop α]

theorem u_eq_top {l : α → β} {u : β → α} (gc : GaloisConnection l u) {x} : u x = ⊤ ↔ l ⊤ ≤ x :=
  top_le_iff.symm.trans gc.le_iff_le.symm
#align galois_connection.u_eq_top GaloisConnection.u_eq_top

theorem u_top [OrderTop β] {l : α → β} {u : β → α} (gc : GaloisConnection l u) : u ⊤ = ⊤ :=
  gc.u_eq_top.2 le_top
#align galois_connection.u_top GaloisConnection.u_top

end OrderTop

section OrderBot

variable [Preorder α] [PartialOrder β] [OrderBot β]

theorem l_eq_bot {l : α → β} {u : β → α} (gc : GaloisConnection l u) {x} : l x = ⊥ ↔ x ≤ u ⊥ :=
  gc.dual.u_eq_top
#align galois_connection.l_eq_bot GaloisConnection.l_eq_bot

theorem l_bot [OrderBot α] {l : α → β} {u : β → α} (gc : GaloisConnection l u) : l ⊥ = ⊥ :=
  gc.dual.u_top
#align galois_connection.l_bot GaloisConnection.l_bot

end OrderBot

section SemilatticeSup

variable [SemilatticeSup α] [SemilatticeSup β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem l_sup : l (a₁ ⊔ a₂) = l a₁ ⊔ l a₂ :=
  (gc.isLUB_l_image isLUB_pair).unique <| by simp only [image_pair, isLUB_pair]
#align galois_connection.l_sup GaloisConnection.l_sup

end SemilatticeSup

section SemilatticeInf

variable [SemilatticeInf α] [SemilatticeInf β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem u_inf : u (b₁ ⊓ b₂) = u b₁ ⊓ u b₂ :=
  gc.dual.l_sup
#align galois_connection.u_inf GaloisConnection.u_inf

end SemilatticeInf

section CompleteLattice

variable [CompleteLattice α] [CompleteLattice β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem l_supᵢ {f : ι → α} : l (supᵢ f) = ⨆ i, l (f i) :=
  Eq.symm <|
    IsLUB.supᵢ_eq <|
      show IsLUB (range (l ∘ f)) (l (supᵢ f)) by
        rw [range_comp, ← supₛ_range]; exact gc.isLUB_l_image (isLUB_supₛ _)
#align galois_connection.l_supr GaloisConnection.l_supᵢ

theorem l_supᵢ₂ {f : ∀ i, κ i → α} : l (⨆ (i) (j), f i j) = ⨆ (i) (j), l (f i j) := by
  simp_rw [gc.l_supᵢ]
#align galois_connection.l_supr₂ GaloisConnection.l_supᵢ₂

theorem u_infᵢ {f : ι → β} : u (infᵢ f) = ⨅ i, u (f i) :=
  gc.dual.l_supᵢ
#align galois_connection.u_infi GaloisConnection.u_infᵢ

theorem u_infᵢ₂ {f : ∀ i, κ i → β} : u (⨅ (i) (j), f i j) = ⨅ (i) (j), u (f i j) :=
  gc.dual.l_supᵢ₂
#align galois_connection.u_infi₂ GaloisConnection.u_infᵢ₂

theorem l_supₛ {s : Set α} : l (supₛ s) = ⨆ a ∈ s, l a := by simp only [supₛ_eq_supᵢ, gc.l_supᵢ]
#align galois_connection.l_Sup GaloisConnection.l_supₛ

theorem u_infₛ {s : Set β} : u (infₛ s) = ⨅ a ∈ s, u a :=
  gc.dual.l_supₛ
#align galois_connection.u_Inf GaloisConnection.u_infₛ

end CompleteLattice

section LinearOrder

variable [LinearOrder α] [LinearOrder β] {l : α → β} {u : β → α} (gc : GaloisConnection l u)

theorem lt_iff_lt {a : α} {b : β} : b < l a ↔ u b < a :=
  lt_iff_lt_of_le_iff_le (gc a b)
#align galois_connection.lt_iff_lt GaloisConnection.lt_iff_lt

end LinearOrder

-- Constructing Galois connections
section Constructions

protected theorem id [pα : Preorder α] : @GaloisConnection α α pα pα id id := fun _ _ =>
  Iff.intro (fun x => x) fun x => x
#align galois_connection.id GaloisConnection.id

protected theorem compose [Preorder α] [Preorder β] [Preorder γ] {l1 : α → β} {u1 : β → α}
    {l2 : β → γ} {u2 : γ → β} (gc1 : GaloisConnection l1 u1) (gc2 : GaloisConnection l2 u2) :
    GaloisConnection (l2 ∘ l1) (u1 ∘ u2) := by intro a b; dsimp; rw [gc2, gc1]
#align galois_connection.compose GaloisConnection.compose

protected theorem dfun {ι : Type u} {α : ι → Type v} {β : ι → Type w} [∀ i, Preorder (α i)]
    [∀ i, Preorder (β i)] (l : ∀ i, α i → β i) (u : ∀ i, β i → α i)
    (gc : ∀ i, GaloisConnection (l i) (u i)) :
    GaloisConnection (fun (a : ∀ i, α i) i => l i (a i)) fun b i => u i (b i) := fun a b =>
  forall_congr' fun i => gc i (a i) (b i)
#align galois_connection.dfun GaloisConnection.dfun

end Constructions

theorem l_comm_of_u_comm {X : Type _} [Preorder X] {Y : Type _} [Preorder Y] {Z : Type _}
    [Preorder Z] {W : Type _} [PartialOrder W] {lYX : X → Y} {uXY : Y → X}
    (hXY : GaloisConnection lYX uXY) {lWZ : Z → W} {uZW : W → Z} (hZW : GaloisConnection lWZ uZW)
    {lWY : Y → W} {uYW : W → Y} (hWY : GaloisConnection lWY uYW) {lZX : X → Z} {uXZ : Z → X}
    (hXZ : GaloisConnection lZX uXZ) (h : ∀ w, uXZ (uZW w) = uXY (uYW w)) {x : X} :
    lWZ (lZX x) = lWY (lYX x) :=
  (hXZ.compose hZW).l_unique (hXY.compose hWY) h
#align galois_connection.l_comm_of_u_comm GaloisConnection.l_comm_of_u_comm

theorem u_comm_of_l_comm {X : Type _} [PartialOrder X] {Y : Type _} [Preorder Y] {Z : Type _}
    [Preorder Z] {W : Type _} [Preorder W] {lYX : X → Y} {uXY : Y → X}
    (hXY : GaloisConnection lYX uXY) {lWZ : Z → W} {uZW : W → Z} (hZW : GaloisConnection lWZ uZW)
    {lWY : Y → W} {uYW : W → Y} (hWY : GaloisConnection lWY uYW) {lZX : X → Z} {uXZ : Z → X}
    (hXZ : GaloisConnection lZX uXZ) (h : ∀ x, lWZ (lZX x) = lWY (lYX x)) {w : W} :
    uXZ (uZW w) = uXY (uYW w) :=
  (hXZ.compose hZW).u_unique (hXY.compose hWY) h
#align galois_connection.u_comm_of_l_comm GaloisConnection.u_comm_of_l_comm

theorem l_comm_iff_u_comm {X : Type _} [PartialOrder X] {Y : Type _} [Preorder Y] {Z : Type _}
    [Preorder Z] {W : Type _} [PartialOrder W] {lYX : X → Y} {uXY : Y → X}
    (hXY : GaloisConnection lYX uXY) {lWZ : Z → W} {uZW : W → Z} (hZW : GaloisConnection lWZ uZW)
    {lWY : Y → W} {uYW : W → Y} (hWY : GaloisConnection lWY uYW) {lZX : X → Z} {uXZ : Z → X}
    (hXZ : GaloisConnection lZX uXZ) :
    (∀ w : W, uXZ (uZW w) = uXY (uYW w)) ↔ ∀ x : X, lWZ (lZX x) = lWY (lYX x) :=
  ⟨hXY.l_comm_of_u_comm hZW hWY hXZ, hXY.u_comm_of_l_comm hZW hWY hXZ⟩
#align galois_connection.l_comm_iff_u_comm GaloisConnection.l_comm_iff_u_comm

end GaloisConnection

section

variable [CompleteLattice α] [CompleteLattice β] [CompleteLattice γ] {f : α → β → γ} {s : Set α}
  {t : Set β} {l u : α → β → γ} {l₁ u₁ : β → γ → α} {l₂ u₂ : α → γ → β}

theorem supₛ_image2_eq_supₛ_supₛ (h₁ : ∀ b, GaloisConnection (swap l b) (u₁ b))
    (h₂ : ∀ a, GaloisConnection (l a) (u₂ a)) : supₛ (image2 l s t) = l (supₛ s) (supₛ t) := by
  simp_rw [supₛ_image2, ← (h₂ _).l_supₛ, ← (h₁ _).l_supₛ]
#align Sup_image2_eq_Sup_Sup supₛ_image2_eq_supₛ_supₛ

theorem supₛ_image2_eq_supₛ_infₛ (h₁ : ∀ b, GaloisConnection (swap l b) (u₁ b))
    (h₂ : ∀ a, GaloisConnection (l a ∘ ofDual) (toDual ∘ u₂ a)) :
    supₛ (image2 l s t) = l (supₛ s) (infₛ t) :=
  @supₛ_image2_eq_supₛ_supₛ _ βᵒᵈ _ _ _ _ _ _ _ _ _ h₁ h₂
#align Sup_image2_eq_Sup_Inf supₛ_image2_eq_supₛ_infₛ

theorem supₛ_image2_eq_infₛ_supₛ (h₁ : ∀ b, GaloisConnection (swap l b ∘ ofDual) (toDual ∘ u₁ b))
    (h₂ : ∀ a, GaloisConnection (l a) (u₂ a)) : supₛ (image2 l s t) = l (infₛ s) (supₛ t) :=
  @supₛ_image2_eq_supₛ_supₛ αᵒᵈ _ _ _ _ _ _ _ _ _ _ h₁ h₂
#align Sup_image2_eq_Inf_Sup supₛ_image2_eq_infₛ_supₛ

theorem supₛ_image2_eq_infₛ_infₛ (h₁ : ∀ b, GaloisConnection (swap l b ∘ ofDual) (toDual ∘ u₁ b))
    (h₂ : ∀ a, GaloisConnection (l a ∘ ofDual) (toDual ∘ u₂ a)) :
    supₛ (image2 l s t) = l (infₛ s) (infₛ t) :=
  @supₛ_image2_eq_supₛ_supₛ αᵒᵈ βᵒᵈ _ _ _ _ _ _ _ _ _ h₁ h₂
#align Sup_image2_eq_Inf_Inf supₛ_image2_eq_infₛ_infₛ

theorem infₛ_image2_eq_infₛ_infₛ (h₁ : ∀ b, GaloisConnection (l₁ b) (swap u b))
    (h₂ : ∀ a, GaloisConnection (l₂ a) (u a)) : infₛ (image2 u s t) = u (infₛ s) (infₛ t) := by
  simp_rw [infₛ_image2, ← (h₂ _).u_infₛ, ← (h₁ _).u_infₛ]
#align Inf_image2_eq_Inf_Inf infₛ_image2_eq_infₛ_infₛ

theorem infₛ_image2_eq_infₛ_supₛ (h₁ : ∀ b, GaloisConnection (l₁ b) (swap u b))
    (h₂ : ∀ a, GaloisConnection (toDual ∘ l₂ a) (u a ∘ ofDual)) :
    infₛ (image2 u s t) = u (infₛ s) (supₛ t) :=
  @infₛ_image2_eq_infₛ_infₛ _ βᵒᵈ _ _ _ _ _ _ _ _ _ h₁ h₂
#align Inf_image2_eq_Inf_Sup infₛ_image2_eq_infₛ_supₛ

theorem infₛ_image2_eq_supₛ_infₛ (h₁ : ∀ b, GaloisConnection (toDual ∘ l₁ b) (swap u b ∘ ofDual))
    (h₂ : ∀ a, GaloisConnection (l₂ a) (u a)) : infₛ (image2 u s t) = u (supₛ s) (infₛ t) :=
  @infₛ_image2_eq_infₛ_infₛ αᵒᵈ _ _ _ _ _ _ _ _ _ _ h₁ h₂
#align Inf_image2_eq_Sup_Inf infₛ_image2_eq_supₛ_infₛ

theorem infₛ_image2_eq_supₛ_supₛ (h₁ : ∀ b, GaloisConnection (toDual ∘ l₁ b) (swap u b ∘ ofDual))
    (h₂ : ∀ a, GaloisConnection (toDual ∘ l₂ a) (u a ∘ ofDual)) :
    infₛ (image2 u s t) = u (supₛ s) (supₛ t) :=
  @infₛ_image2_eq_infₛ_infₛ αᵒᵈ βᵒᵈ _ _ _ _ _ _ _ _ _ h₁ h₂
#align Inf_image2_eq_Sup_Sup infₛ_image2_eq_supₛ_supₛ

end

namespace OrderIso

variable [Preorder α] [Preorder β]

@[simp]
theorem bddAbove_image (e : α ≃o β) {s : Set α} : BddAbove (e '' s) ↔ BddAbove s :=
  e.to_galoisConnection.bddAbove_l_image
#align order_iso.bdd_above_image OrderIso.bddAbove_image

@[simp]
theorem bddBelow_image (e : α ≃o β) {s : Set α} : BddBelow (e '' s) ↔ BddBelow s :=
  e.dual.bddAbove_image
#align order_iso.bdd_below_image OrderIso.bddBelow_image

@[simp]
theorem bddAbove_preimage (e : α ≃o β) {s : Set β} : BddAbove (e ⁻¹' s) ↔ BddAbove s := by
  rw [← e.bddAbove_image, e.image_preimage]
#align order_iso.bdd_above_preimage OrderIso.bddAbove_preimage

@[simp]
theorem bddBelow_preimage (e : α ≃o β) {s : Set β} : BddBelow (e ⁻¹' s) ↔ BddBelow s := by
  rw [← e.bddBelow_image, e.image_preimage]
#align order_iso.bdd_below_preimage OrderIso.bddBelow_preimage

end OrderIso

namespace Nat

theorem galoisConnection_mul_div {k : ℕ} (h : 0 < k) :
    GaloisConnection (fun n => n * k) fun n => n / k := fun _ _ => (le_div_iff_mul_le h).symm
#align nat.galois_connection_mul_div Nat.galoisConnection_mul_div

end Nat

-- Porting note: this used to have a `@[nolint has_nonempty_instance]`
/-- A Galois insertion is a Galois connection where `l ∘ u = id`. It also contains a constructive
choice function, to give better definitional equalities when lifting order structures. Dual
to `GaloisCoinsertion` -/
structure GaloisInsertion {α β : Type _} [Preorder α] [Preorder β] (l : α → β) (u : β → α) where
  /-- A contructive choice function for images of `l`. -/
  choice : ∀ x : α, u (l x) ≤ x → β
  /-- The Galois connection associated to a Galois insertion. -/
  gc : GaloisConnection l u
  /-- Main property of a Galois insertion. -/
  le_l_u : ∀ x, x ≤ l (u x)
  /-- Property of the choice function. -/
  choice_eq : ∀ a h, choice a h = l a
#align galois_insertion GaloisInsertion

/-- A constructor for a Galois insertion with the trivial `choice` function. -/
def GaloisInsertion.monotoneIntro {α β : Type _} [Preorder α] [Preorder β] {l : α → β} {u : β → α}
    (hu : Monotone u) (hl : Monotone l) (hul : ∀ a, a ≤ u (l a)) (hlu : ∀ b, l (u b) = b) :
    GaloisInsertion l u where
  choice x _ := l x
  gc := GaloisConnection.monotone_intro hu hl hul fun b => le_of_eq (hlu b)
  le_l_u b := le_of_eq <| (hlu b).symm
  choice_eq _ _ := rfl
#align galois_insertion.monotone_intro GaloisInsertion.monotoneIntro

/-- Makes a Galois insertion from an order-preserving bijection. -/
protected def OrderIso.toGaloisInsertion [Preorder α] [Preorder β] (oi : α ≃o β) :
    GaloisInsertion oi oi.symm where
  choice b _ := oi b
  gc := oi.to_galoisConnection
  le_l_u g := le_of_eq (oi.right_inv g).symm
  choice_eq _ _ := rfl
#align order_iso.to_galois_insertion OrderIso.toGaloisInsertion

/-- Make a `GaloisInsertion l u` from a `GaloisConnection l u` such that `∀ b, b ≤ l (u b)` -/
def GaloisConnection.toGaloisInsertion {α β : Type _} [Preorder α] [Preorder β] {l : α → β}
    {u : β → α} (gc : GaloisConnection l u) (h : ∀ b, b ≤ l (u b)) : GaloisInsertion l u :=
  { choice := fun x _ => l x
    gc
    le_l_u := h
    choice_eq := fun _ _ => rfl }
#align galois_connection.to_galois_insertion GaloisConnection.toGaloisInsertion

/-- Lift the bottom along a Galois connection -/
def GaloisConnection.liftOrderBot {α β : Type _} [Preorder α] [OrderBot α] [PartialOrder β]
    {l : α → β} {u : β → α} (gc : GaloisConnection l u) :
    OrderBot β where
  bot := l ⊥
  bot_le _ := gc.l_le <| bot_le
#align galois_connection.lift_order_bot GaloisConnection.liftOrderBot

namespace GaloisInsertion

variable {l : α → β} {u : β → α}

theorem l_u_eq [Preorder α] [PartialOrder β] (gi : GaloisInsertion l u) (b : β) : l (u b) = b :=
  (gi.gc.l_u_le _).antisymm (gi.le_l_u _)
#align galois_insertion.l_u_eq GaloisInsertion.l_u_eq

theorem leftInverse_l_u [Preorder α] [PartialOrder β] (gi : GaloisInsertion l u) :
    LeftInverse l u :=
  gi.l_u_eq
#align galois_insertion.left_inverse_l_u GaloisInsertion.leftInverse_l_u

theorem l_surjective [Preorder α] [PartialOrder β] (gi : GaloisInsertion l u) : Surjective l :=
  gi.leftInverse_l_u.surjective
#align galois_insertion.l_surjective GaloisInsertion.l_surjective

theorem u_injective [Preorder α] [PartialOrder β] (gi : GaloisInsertion l u) : Injective u :=
  gi.leftInverse_l_u.injective
#align galois_insertion.u_injective GaloisInsertion.u_injective

theorem l_sup_u [SemilatticeSup α] [SemilatticeSup β] (gi : GaloisInsertion l u) (a b : β) :
    l (u a ⊔ u b) = a ⊔ b :=
  calc
    l (u a ⊔ u b) = l (u a) ⊔ l (u b) := gi.gc.l_sup
    _ = a ⊔ b := by simp only [gi.l_u_eq]

#align galois_insertion.l_sup_u GaloisInsertion.l_sup_u

theorem l_supᵢ_u [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u) {ι : Sort x}
    (f : ι → β) : l (⨆ i, u (f i)) = ⨆ i, f i :=
  calc
    l (⨆ i : ι, u (f i)) = ⨆ i : ι, l (u (f i)) := gi.gc.l_supᵢ
    _ = ⨆ i : ι, f i := congr_arg _ <| funext fun i => gi.l_u_eq (f i)

#align galois_insertion.l_supr_u GaloisInsertion.l_supᵢ_u

theorem l_bsupᵢ_u [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u) {ι : Sort x}
    {p : ι → Prop} (f : ∀ (i) (_ : p i), β) : l (⨆ (i) (hi), u (f i hi)) = ⨆ (i) (hi), f i hi := by
  simp only [supᵢ_subtype', gi.l_supᵢ_u]
#align galois_insertion.l_bsupr_u GaloisInsertion.l_bsupᵢ_u

theorem l_supₛ_u_image [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u)
    (s : Set β) : l (supₛ (u '' s)) = supₛ s := by rw [supₛ_image, gi.l_bsupᵢ_u, supₛ_eq_supᵢ]
#align galois_insertion.l_Sup_u_image GaloisInsertion.l_supₛ_u_image

theorem l_inf_u [SemilatticeInf α] [SemilatticeInf β] (gi : GaloisInsertion l u) (a b : β) :
    l (u a ⊓ u b) = a ⊓ b :=
  calc
    l (u a ⊓ u b) = l (u (a ⊓ b)) := congr_arg l gi.gc.u_inf.symm
    _ = a ⊓ b := by simp only [gi.l_u_eq]

#align galois_insertion.l_inf_u GaloisInsertion.l_inf_u

theorem l_infᵢ_u [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u) {ι : Sort x}
    (f : ι → β) : l (⨅ i, u (f i)) = ⨅ i, f i :=
  calc
    l (⨅ i : ι, u (f i)) = l (u (⨅ i : ι, f i)) := congr_arg l gi.gc.u_infᵢ.symm
    _ = ⨅ i : ι, f i := gi.l_u_eq _

#align galois_insertion.l_infi_u GaloisInsertion.l_infᵢ_u

theorem l_binfᵢ_u [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u) {ι : Sort x}
    {p : ι → Prop} (f : ∀ (i) (_ : p i), β) : l (⨅ (i) (hi), u (f i hi)) = ⨅ (i) (hi), f i hi := by
  simp only [infᵢ_subtype', gi.l_infᵢ_u]
#align galois_insertion.l_binfi_u GaloisInsertion.l_binfᵢ_u

theorem l_infₛ_u_image [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u)
    (s : Set β) : l (infₛ (u '' s)) = infₛ s := by rw [infₛ_image, gi.l_binfᵢ_u, infₛ_eq_infᵢ]
#align galois_insertion.l_Inf_u_image GaloisInsertion.l_infₛ_u_image

theorem l_infᵢ_of_ul_eq_self [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u)
    {ι : Sort x} (f : ι → α) (hf : ∀ i, u (l (f i)) = f i) : l (⨅ i, f i) = ⨅ i, l (f i) :=
  calc
    l (⨅ i, f i) = l (⨅ i : ι, u (l (f i))) := by simp [hf]
    _ = ⨅ i, l (f i) := gi.l_infᵢ_u _

#align galois_insertion.l_infi_of_ul_eq_self GaloisInsertion.l_infᵢ_of_ul_eq_self

theorem l_binfᵢ_of_ul_eq_self [CompleteLattice α] [CompleteLattice β] (gi : GaloisInsertion l u)
    {ι : Sort x} {p : ι → Prop} (f : ∀ (i) (_ : p i), α) (hf : ∀ i hi, u (l (f i hi)) = f i hi) :
    l (⨅ (i) (hi), f i hi) = ⨅ (i) (hi), l (f i hi) := by
  rw [infᵢ_subtype', infᵢ_subtype']
  exact gi.l_infᵢ_of_ul_eq_self _ fun _ => hf _ _
#align galois_insertion.l_binfi_of_ul_eq_self GaloisInsertion.l_binfᵢ_of_ul_eq_self

theorem u_le_u_iff [Preorder α] [Preorder β] (gi : GaloisInsertion l u) {a b} : u a ≤ u b ↔ a ≤ b :=
  ⟨fun h => (gi.le_l_u _).trans (gi.gc.l_le h), fun h => gi.gc.monotone_u h⟩
#align galois_insertion.u_le_u_iff GaloisInsertion.u_le_u_iff

theorem strictMono_u [Preorder α] [Preorder β] (gi : GaloisInsertion l u) : StrictMono u :=
  strictMono_of_le_iff_le fun _ _ => gi.u_le_u_iff.symm
#align galois_insertion.strict_mono_u GaloisInsertion.strictMono_u

theorem isLUB_of_u_image [Preorder α] [Preorder β] (gi : GaloisInsertion l u) {s : Set β} {a : α}
    (hs : IsLUB (u '' s) a) : IsLUB s (l a) :=
  ⟨fun x hx => (gi.le_l_u x).trans <| gi.gc.monotone_l <| hs.1 <| mem_image_of_mem _ hx, fun _ hx =>
    gi.gc.l_le <| hs.2 <| gi.gc.monotone_u.mem_upperBounds_image hx⟩
#align galois_insertion.is_lub_of_u_image GaloisInsertion.isLUB_of_u_image

theorem isGLB_of_u_image [Preorder α] [Preorder β] (gi : GaloisInsertion l u) {s : Set β} {a : α}
    (hs : IsGLB (u '' s) a) : IsGLB s (l a) :=
  ⟨fun _ hx => gi.gc.l_le <| hs.1 <| mem_image_of_mem _ hx, fun x hx =>
    (gi.le_l_u x).trans <| gi.gc.monotone_l <| hs.2 <| gi.gc.monotone_u.mem_lowerBounds_image hx⟩
#align galois_insertion.is_glb_of_u_image GaloisInsertion.isGLB_of_u_image

section lift

variable [PartialOrder β]

-- See note [reducible non instances]
/-- Lift the suprema along a Galois insertion -/
@[reducible]
def liftSemilatticeSup [SemilatticeSup α] (gi : GaloisInsertion l u) : SemilatticeSup β :=
  { ‹PartialOrder β› with
    sup := fun a b => l (u a ⊔ u b)
    le_sup_left := fun a _ => (gi.le_l_u a).trans <| gi.gc.monotone_l <| le_sup_left
    le_sup_right := fun _ b => (gi.le_l_u b).trans <| gi.gc.monotone_l <| le_sup_right
    sup_le := fun a b c hac hbc =>
      gi.gc.l_le <| sup_le (gi.gc.monotone_u hac) (gi.gc.monotone_u hbc) }
#align galois_insertion.lift_semilattice_sup GaloisInsertion.liftSemilatticeSup

-- See note [reducible non instances]
/-- Lift the infima along a Galois insertion -/
@[reducible]
def liftSemilatticeInf [SemilatticeInf α] (gi : GaloisInsertion l u) : SemilatticeInf β :=
  { ‹PartialOrder β› with
    inf := fun a b =>
      gi.choice (u a ⊓ u b) <|
        le_inf (gi.gc.monotone_u <| gi.gc.l_le <| inf_le_left)
          (gi.gc.monotone_u <| gi.gc.l_le <| inf_le_right)
    inf_le_left := by simp only [gi.choice_eq]; exact fun a b => gi.gc.l_le inf_le_left
    inf_le_right := by simp only [gi.choice_eq]; exact fun a b => gi.gc.l_le inf_le_right
    le_inf := by
      simp only [gi.choice_eq]
      exact fun a b c hac hbc =>
        (gi.le_l_u a).trans <|
          gi.gc.monotone_l <| le_inf (gi.gc.monotone_u hac) (gi.gc.monotone_u hbc) }
#align galois_insertion.lift_semilattice_inf GaloisInsertion.liftSemilatticeInf

-- See note [reducible non instances]
/-- Lift the suprema and infima along a Galois insertion -/
@[reducible]
def liftLattice [Lattice α] (gi : GaloisInsertion l u) : Lattice β :=
  { gi.liftSemilatticeSup, gi.liftSemilatticeInf with }
#align galois_insertion.lift_lattice GaloisInsertion.liftLattice

-- See note [reducible non instances]
/-- Lift the top along a Galois insertion -/
@[reducible]
def liftOrderTop [Preorder α] [OrderTop α] (gi : GaloisInsertion l u) :
    OrderTop β where
  top := gi.choice ⊤ <| le_top
  le_top := by
    simp only [gi.choice_eq]; exact fun b => (gi.le_l_u b).trans (gi.gc.monotone_l le_top)
#align galois_insertion.lift_order_top GaloisInsertion.liftOrderTop

-- See note [reducible non instances]
/-- Lift the top, bottom, suprema, and infima along a Galois insertion -/
@[reducible]
def liftBoundedOrder [Preorder α] [BoundedOrder α] (gi : GaloisInsertion l u) : BoundedOrder β :=
  { gi.liftOrderTop, gi.gc.liftOrderBot with }
#align galois_insertion.lift_bounded_order GaloisInsertion.liftBoundedOrder

-- See note [reducible non instances]
/-- Lift all suprema and infima along a Galois insertion -/
@[reducible]
def liftCompleteLattice [CompleteLattice α] (gi : GaloisInsertion l u) : CompleteLattice β :=
  { gi.liftBoundedOrder, gi.liftLattice with
    supₛ := fun s => l (supₛ (u '' s))
    supₛ_le := fun s => (gi.isLUB_of_u_image (isLUB_supₛ _)).2
    le_supₛ := fun s => (gi.isLUB_of_u_image (isLUB_supₛ _)).1
    infₛ := fun s =>
      gi.choice (infₛ (u '' s)) <|
        (isGLB_infₛ _).2 <|
          gi.gc.monotone_u.mem_lowerBounds_image (gi.isGLB_of_u_image <| isGLB_infₛ _).1
    infₛ_le := fun s => by dsimp; rw [gi.choice_eq]; exact (gi.isGLB_of_u_image (isGLB_infₛ _)).1
    le_infₛ := fun s => by dsimp; rw [gi.choice_eq]; exact (gi.isGLB_of_u_image (isGLB_infₛ _)).2 }
#align galois_insertion.lift_complete_lattice GaloisInsertion.liftCompleteLattice

end lift

end GaloisInsertion

-- Porting note: this used to have a `@[nolint has_nonempty_instance]`
/-- A Galois coinsertion is a Galois connection where `u ∘ l = id`. It also contains a constructive
choice function, to give better definitional equalities when lifting order structures. Dual to
`GaloisInsertion` -/
structure GaloisCoinsertion [Preorder α] [Preorder β] (l : α → β) (u : β → α) where
  /-- A contructive choice function for images of `u`. -/
  choice : ∀ x : β, x ≤ l (u x) → α
  /-- The Galois connection associated to a Galois coinsertion. -/
  gc : GaloisConnection l u
  /-- Main property of a Galois coinsertion. -/
  u_l_le : ∀ x, u (l x) ≤ x
  /-- Property of the choice function. -/
  choice_eq : ∀ a h, choice a h = u a
#align galois_coinsertion GaloisCoinsertion

/-- Make a `GaloisInsertion` between `αᵒᵈ` and `βᵒᵈ` from a `GaloisCoinsertion` between `α` and
`β`. -/
def GaloisCoinsertion.dual [Preorder α] [Preorder β] {l : α → β} {u : β → α} :
    GaloisCoinsertion l u → GaloisInsertion (toDual ∘ u ∘ ofDual) (toDual ∘ l ∘ ofDual) :=
  fun x => ⟨x.1, x.2.dual, x.3, x.4⟩
#align galois_coinsertion.dual GaloisCoinsertion.dual

/-- Make a `GaloisCoinsertion` between `αᵒᵈ` and `βᵒᵈ` from a `GaloisInsertion` between `α` and
`β`. -/
def GaloisInsertion.dual [Preorder α] [Preorder β] {l : α → β} {u : β → α} :
    GaloisInsertion l u → GaloisCoinsertion (toDual ∘ u ∘ ofDual) (toDual ∘ l ∘ ofDual) :=
  fun x => ⟨x.1, x.2.dual, x.3, x.4⟩
#align galois_insertion.dual GaloisInsertion.dual

/-- Make a `GaloisInsertion` between `α` and `β` from a `GaloisCoinsertion` between `αᵒᵈ` and
`βᵒᵈ`. -/
def GaloisCoinsertion.ofDual [Preorder α] [Preorder β] {l : αᵒᵈ → βᵒᵈ} {u : βᵒᵈ → αᵒᵈ} :
    GaloisCoinsertion l u → GaloisInsertion (ofDual ∘ u ∘ toDual) (ofDual ∘ l ∘ toDual) :=
  fun x => ⟨x.1, x.2.dual, x.3, x.4⟩
#align galois_coinsertion.of_dual GaloisCoinsertion.ofDual

/-- Make a `GaloisCoinsertion` between `α` and `β` from a `GaloisInsertion` between `αᵒᵈ` and
`βᵒᵈ`. -/
def GaloisInsertion.ofDual [Preorder α] [Preorder β] {l : αᵒᵈ → βᵒᵈ} {u : βᵒᵈ → αᵒᵈ} :
    GaloisInsertion l u → GaloisCoinsertion (ofDual ∘ u ∘ toDual) (ofDual ∘ l ∘ toDual) :=
  fun x => ⟨x.1, x.2.dual, x.3, x.4⟩
#align galois_insertion.of_dual GaloisInsertion.ofDual

/-- Makes a Galois coinsertion from an order-preserving bijection. -/
protected def OrderIso.toGaloisCoinsertion [Preorder α] [Preorder β] (oi : α ≃o β) :
    GaloisCoinsertion oi oi.symm where
  choice b _ := oi.symm b
  gc := oi.to_galoisConnection
  u_l_le g := le_of_eq (oi.left_inv g)
  choice_eq _ _ := rfl
#align order_iso.to_galois_coinsertion OrderIso.toGaloisCoinsertion

/-- A constructor for a Galois coinsertion with the trivial `choice` function. -/
def GaloisCoinsertion.monotoneIntro [Preorder α] [Preorder β] {l : α → β} {u : β → α}
    (hu : Monotone u) (hl : Monotone l) (hlu : ∀ b, l (u b) ≤ b) (hul : ∀ a, u (l a) = a) :
    GaloisCoinsertion l u :=
  (GaloisInsertion.monotoneIntro hl.dual hu.dual hlu hul).ofDual
#align galois_coinsertion.monotone_intro GaloisCoinsertion.monotoneIntro

/-- Make a `GaloisCoinsertion l u` from a `GaloisConnection l u` such that `∀ b, b ≤ l (u b)` -/
def GaloisConnection.toGaloisCoinsertion {α β : Type _} [Preorder α] [Preorder β] {l : α → β}
    {u : β → α} (gc : GaloisConnection l u) (h : ∀ a, u (l a) ≤ a) : GaloisCoinsertion l u :=
  { choice := fun x _ => u x
    gc
    u_l_le := h
    choice_eq := fun _ _ => rfl }
#align galois_connection.to_galois_coinsertion GaloisConnection.toGaloisCoinsertion

/-- Lift the top along a Galois connection -/
def GaloisConnection.liftOrderTop {α β : Type _} [PartialOrder α] [Preorder β] [OrderTop β]
    {l : α → β} {u : β → α} (gc : GaloisConnection l u) :
    OrderTop α where
  top := u ⊤
  le_top _ := gc.le_u <| le_top
#align galois_connection.lift_order_top GaloisConnection.liftOrderTop

namespace GaloisCoinsertion

variable {l : α → β} {u : β → α}

theorem u_l_eq [PartialOrder α] [Preorder β] (gi : GaloisCoinsertion l u) (a : α) : u (l a) = a :=
  gi.dual.l_u_eq a
#align galois_coinsertion.u_l_eq GaloisCoinsertion.u_l_eq

theorem u_l_leftInverse [PartialOrder α] [Preorder β] (gi : GaloisCoinsertion l u) :
    LeftInverse u l :=
  gi.u_l_eq
#align galois_coinsertion.u_l_left_inverse GaloisCoinsertion.u_l_leftInverse

theorem u_surjective [PartialOrder α] [Preorder β] (gi : GaloisCoinsertion l u) : Surjective u :=
  gi.dual.l_surjective
#align galois_coinsertion.u_surjective GaloisCoinsertion.u_surjective

theorem l_injective [PartialOrder α] [Preorder β] (gi : GaloisCoinsertion l u) : Injective l :=
  gi.dual.u_injective
#align galois_coinsertion.l_injective GaloisCoinsertion.l_injective

theorem u_inf_l [SemilatticeInf α] [SemilatticeInf β] (gi : GaloisCoinsertion l u) (a b : α) :
    u (l a ⊓ l b) = a ⊓ b :=
  gi.dual.l_sup_u a b
#align galois_coinsertion.u_inf_l GaloisCoinsertion.u_inf_l

theorem u_infᵢ_l [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u) {ι : Sort x}
    (f : ι → α) : u (⨅ i, l (f i)) = ⨅ i, f i :=
  gi.dual.l_supᵢ_u _
#align galois_coinsertion.u_infi_l GaloisCoinsertion.u_infᵢ_l

theorem u_infₛ_l_image [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u)
    (s : Set α) : u (infₛ (l '' s)) = infₛ s :=
  gi.dual.l_supₛ_u_image _
#align galois_coinsertion.u_Inf_l_image GaloisCoinsertion.u_infₛ_l_image

theorem u_sup_l [SemilatticeSup α] [SemilatticeSup β] (gi : GaloisCoinsertion l u) (a b : α) :
    u (l a ⊔ l b) = a ⊔ b :=
  gi.dual.l_inf_u _ _
#align galois_coinsertion.u_sup_l GaloisCoinsertion.u_sup_l

theorem u_supᵢ_l [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u) {ι : Sort x}
    (f : ι → α) : u (⨆ i, l (f i)) = ⨆ i, f i :=
  gi.dual.l_infᵢ_u _
#align galois_coinsertion.u_supr_l GaloisCoinsertion.u_supᵢ_l

theorem u_bsupᵢ_l [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u) {ι : Sort x}
    {p : ι → Prop} (f : ∀ (i) (_ : p i), α) : u (⨆ (i) (hi), l (f i hi)) = ⨆ (i) (hi), f i hi :=
  gi.dual.l_binfᵢ_u _
#align galois_coinsertion.u_bsupr_l GaloisCoinsertion.u_bsupᵢ_l

theorem u_supₛ_l_image [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u)
    (s : Set α) : u (supₛ (l '' s)) = supₛ s :=
  gi.dual.l_infₛ_u_image _
#align galois_coinsertion.u_Sup_l_image GaloisCoinsertion.u_supₛ_l_image

theorem u_supᵢ_of_lu_eq_self [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u)
    {ι : Sort x} (f : ι → β) (hf : ∀ i, l (u (f i)) = f i) : u (⨆ i, f i) = ⨆ i, u (f i) :=
  gi.dual.l_infᵢ_of_ul_eq_self _ hf
#align galois_coinsertion.u_supr_of_lu_eq_self GaloisCoinsertion.u_supᵢ_of_lu_eq_self

theorem u_bsupᵢ_of_lu_eq_self [CompleteLattice α] [CompleteLattice β] (gi : GaloisCoinsertion l u)
    {ι : Sort x} {p : ι → Prop} (f : ∀ (i) (_ : p i), β) (hf : ∀ i hi, l (u (f i hi)) = f i hi) :
    u (⨆ (i) (hi), f i hi) = ⨆ (i) (hi), u (f i hi) :=
  gi.dual.l_binfᵢ_of_ul_eq_self _ hf
#align galois_coinsertion.u_bsupr_of_lu_eq_self GaloisCoinsertion.u_bsupᵢ_of_lu_eq_self

theorem l_le_l_iff [Preorder α] [Preorder β] (gi : GaloisCoinsertion l u) {a b} :
    l a ≤ l b ↔ a ≤ b :=
  gi.dual.u_le_u_iff
#align galois_coinsertion.l_le_l_iff GaloisCoinsertion.l_le_l_iff

theorem strictMono_l [Preorder α] [Preorder β] (gi : GaloisCoinsertion l u) : StrictMono l :=
  fun _ _ h => gi.dual.strictMono_u h
#align galois_coinsertion.strict_mono_l GaloisCoinsertion.strictMono_l

theorem isGLB_of_l_image [Preorder α] [Preorder β] (gi : GaloisCoinsertion l u) {s : Set α} {a : β}
    (hs : IsGLB (l '' s) a) : IsGLB s (u a) :=
  gi.dual.isLUB_of_u_image hs
#align galois_coinsertion.is_glb_of_l_image GaloisCoinsertion.isGLB_of_l_image

theorem isLUB_of_l_image [Preorder α] [Preorder β] (gi : GaloisCoinsertion l u) {s : Set α} {a : β}
    (hs : IsLUB (l '' s) a) : IsLUB s (u a) :=
  gi.dual.isGLB_of_u_image hs
#align galois_coinsertion.is_lub_of_l_image GaloisCoinsertion.isLUB_of_l_image

section lift

variable [PartialOrder α]

-- Porting note: In `liftSemilatticeInf` and `liftSemilatticeSup` below, the elaborator
-- seems to struggle with αᵒᵈ vs α; the `by exact`s are not present in Lean 3, but without
-- them the declarations compile much more slowly for some reason.
-- Possibly related to the issue discussed at
-- https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Performance.20issue.20with.20.60CompleteBooleanAlgebra.60/near/316760798

-- See note [reducible non instances]
/-- Lift the infima along a Galois coinsertion -/
@[reducible]
def liftSemilatticeInf [SemilatticeInf β] (gi : GaloisCoinsertion l u) : SemilatticeInf α :=
  { ‹PartialOrder α› with
    inf_le_left := fun a b => by
      exact (@OrderDual.semilatticeInf αᵒᵈ gi.dual.liftSemilatticeSup).inf_le_left a b
    inf_le_right := fun a b => by
      exact (@OrderDual.semilatticeInf αᵒᵈ gi.dual.liftSemilatticeSup).inf_le_right a b
    le_inf := fun a b c => by
      exact (@OrderDual.semilatticeInf αᵒᵈ gi.dual.liftSemilatticeSup).le_inf a b c
    inf := fun a b => u (l a ⊓ l b) }
#align galois_coinsertion.lift_semilattice_inf GaloisCoinsertion.liftSemilatticeInf

-- See note [reducible non instances]
/-- Lift the suprema along a Galois coinsertion -/
@[reducible]
def liftSemilatticeSup [SemilatticeSup β] (gi : GaloisCoinsertion l u) : SemilatticeSup α :=
  { ‹PartialOrder α› with
    sup := fun a b =>
      gi.choice (l a ⊔ l b) <|
        sup_le (gi.gc.monotone_l <| gi.gc.le_u <| le_sup_left)
          (gi.gc.monotone_l <| gi.gc.le_u <| le_sup_right)
    le_sup_left := fun a b => by
      exact (@OrderDual.semilatticeSup αᵒᵈ gi.dual.liftSemilatticeInf).le_sup_left a b
    le_sup_right := fun a b => by
      exact (@OrderDual.semilatticeSup αᵒᵈ gi.dual.liftSemilatticeInf).le_sup_right a b
    sup_le := fun a b c => by
      exact (@OrderDual.semilatticeSup αᵒᵈ gi.dual.liftSemilatticeInf).sup_le a b c }
#align galois_coinsertion.lift_semilattice_sup GaloisCoinsertion.liftSemilatticeSup

-- See note [reducible non instances]
/-- Lift the suprema and infima along a Galois coinsertion -/
@[reducible]
def liftLattice [Lattice β] (gi : GaloisCoinsertion l u) : Lattice α :=
  { gi.liftSemilatticeSup, gi.liftSemilatticeInf with }
#align galois_coinsertion.lift_lattice GaloisCoinsertion.liftLattice

-- See note [reducible non instances]
/-- Lift the bot along a Galois coinsertion -/
@[reducible]
def liftOrderBot [Preorder β] [OrderBot β] (gi : GaloisCoinsertion l u) : OrderBot α :=
  { @OrderDual.orderBot _ _ gi.dual.liftOrderTop with bot := gi.choice ⊥ <| bot_le }
#align galois_coinsertion.lift_order_bot GaloisCoinsertion.liftOrderBot

-- See note [reducible non instances]
/-- Lift the top, bottom, suprema, and infima along a Galois coinsertion -/
@[reducible]
def liftBoundedOrder [Preorder β] [BoundedOrder β] (gi : GaloisCoinsertion l u) : BoundedOrder α :=
  { gi.liftOrderBot, gi.gc.liftOrderTop with }
#align galois_coinsertion.lift_bounded_order GaloisCoinsertion.liftBoundedOrder

-- See note [reducible non instances]
/-- Lift all suprema and infima along a Galois coinsertion -/
@[reducible]
def liftCompleteLattice [CompleteLattice β] (gi : GaloisCoinsertion l u) : CompleteLattice α :=
  { @OrderDual.completeLattice αᵒᵈ gi.dual.liftCompleteLattice with
    infₛ := fun s => u (infₛ (l '' s))
    supₛ := fun s => gi.choice (supₛ (l '' s)) _ }
#align galois_coinsertion.lift_complete_lattice GaloisCoinsertion.liftCompleteLattice

end lift

end GaloisCoinsertion

/-- If `α` is a partial order with bottom element (e.g., `ℕ`, `ℝ≥0`), then `WithBot.unbot' ⊥` and
coercion form a Galois insertion. -/
def WithBot.giUnbot'Bot [Preorder α] [OrderBot α] :
    GaloisInsertion (WithBot.unbot' ⊥) (some : α → WithBot α) where
  gc _ _ := WithBot.unbot'_bot_le_iff
  le_l_u _ := le_rfl
  choice o _ := o.unbot' ⊥
  choice_eq _ _ := rfl
#align with_bot.gi_unbot'_bot WithBot.giUnbot'Bot