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

/-!
# Preparations for defining operations on `finset`.

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

The operations here ignore multiplicities,
and preparatory for defining the corresponding operations on `finset`.
-/

namespace multiset
open list

variables {α : Type*} [decidable_eq α] {s : multiset α}
/-! ### finset insert -/

/-- `ndinsert a s` is the lift of the list `insert` operation. This operation
  does not respect multiplicities, unlike `cons`, but it is suitable as
  an insert operation on `finset`. -/
def ndinsert (a : α) (s : multiset α) : multiset α :=
quot.lift_on s (λ l, (l.insert a : multiset α))
  (λ s t p, quot.sound (p.insert a))

@[simp] theorem coe_ndinsert (a : α) (l : list α) : ndinsert a l = (insert a l : list α) := rfl

@[simp] theorem ndinsert_zero (a : α) : ndinsert a 0 = {a} := rfl

@[simp, priority 980]
theorem ndinsert_of_mem {a : α} {s : multiset α} : a ∈ s → ndinsert a s = s :=
quot.induction_on s $ λ l h, congr_arg coe $ insert_of_mem h

@[simp, priority 980]
theorem ndinsert_of_not_mem {a : α} {s : multiset α} : a ∉ s → ndinsert a s = a ::ₘ s :=
quot.induction_on s $ λ l h, congr_arg coe $ insert_of_not_mem h

@[simp] theorem mem_ndinsert {a b : α} {s : multiset α} : a ∈ ndinsert b s ↔ a = b ∨ a ∈ s :=
quot.induction_on s $ λ l, mem_insert_iff

@[simp] theorem le_ndinsert_self (a : α) (s : multiset α) : s ≤ ndinsert a s :=
quot.induction_on s $ λ l, (sublist_insert _ _).subperm

@[simp] theorem mem_ndinsert_self (a : α) (s : multiset α) : a ∈ ndinsert a s :=
mem_ndinsert.2 (or.inl rfl)

theorem mem_ndinsert_of_mem {a b : α} {s : multiset α} (h : a ∈ s) : a ∈ ndinsert b s :=
mem_ndinsert.2 (or.inr h)

@[simp, priority 980]
theorem length_ndinsert_of_mem {a : α} {s : multiset α} (h : a ∈ s) :
  card (ndinsert a s) = card s :=
by simp [h]

@[simp, priority 980]
theorem length_ndinsert_of_not_mem {a : α} {s : multiset α} (h : a ∉ s) :
  card (ndinsert a s) = card s + 1 :=
by simp [h]

theorem dedup_cons {a : α} {s : multiset α} :
  dedup (a ::ₘ s) = ndinsert a (dedup s) :=
by by_cases a ∈ s; simp [h]

lemma nodup.ndinsert (a : α) : nodup s → nodup (ndinsert a s) :=
quot.induction_on s $ λ l, nodup.insert

theorem ndinsert_le {a : α} {s t : multiset α} : ndinsert a s ≤ t ↔ s ≤ t ∧ a ∈ t :=
⟨λ h, ⟨le_trans (le_ndinsert_self _ _) h, mem_of_le h (mem_ndinsert_self _ _)⟩,
 λ ⟨l, m⟩, if h : a ∈ s then by simp [h, l] else
   by rw [ndinsert_of_not_mem h, ← cons_erase m, cons_le_cons_iff,
          ← le_cons_of_not_mem h, cons_erase m]; exact l⟩

lemma attach_ndinsert (a : α) (s : multiset α) :
  (s.ndinsert a).attach =
    ndinsert ⟨a, mem_ndinsert_self a s⟩ (s.attach.map $ λp, ⟨p.1, mem_ndinsert_of_mem p.2⟩) :=
have eq : ∀h : ∀(p : {x // x ∈ s}), p.1 ∈ s,
    (λ (p : {x // x ∈ s}), ⟨p.val, h p⟩ : {x // x ∈ s} → {x // x ∈ s}) = id, from
  assume h, funext $ assume p, subtype.eq rfl,
have ∀t (eq : s.ndinsert a = t), t.attach = ndinsert ⟨a, eq ▸ mem_ndinsert_self a s⟩
  (s.attach.map $ λp, ⟨p.1, eq ▸ mem_ndinsert_of_mem p.2⟩),
begin
  intros t ht,
  by_cases a ∈ s,
  { rw [ndinsert_of_mem h] at ht,
    subst ht,
    rw [eq, map_id, ndinsert_of_mem (mem_attach _ _)] },
  { rw [ndinsert_of_not_mem h] at ht,
    subst ht,
    simp [attach_cons, h] }
end,
this _ rfl

@[simp] theorem disjoint_ndinsert_left {a : α} {s t : multiset α} :
  disjoint (ndinsert a s) t ↔ a ∉ t ∧ disjoint s t :=
iff.trans (by simp [disjoint]) disjoint_cons_left

@[simp] theorem disjoint_ndinsert_right {a : α} {s t : multiset α} :
  disjoint s (ndinsert a t) ↔ a ∉ s ∧ disjoint s t :=
by rw [disjoint_comm, disjoint_ndinsert_left]; tauto

/-! ### finset union -/

/-- `ndunion s t` is the lift of the list `union` operation. This operation
  does not respect multiplicities, unlike `s ∪ t`, but it is suitable as
  a union operation on `finset`. (`s ∪ t` would also work as a union operation
  on finset, but this is more efficient.) -/
def ndunion (s t : multiset α) : multiset α :=
quotient.lift_on₂ s t (λ l₁ l₂, (l₁.union l₂ : multiset α)) $ λ v₁ v₂ w₁ w₂ p₁ p₂,
  quot.sound $ p₁.union p₂

@[simp] theorem coe_ndunion (l₁ l₂ : list α) : @ndunion α _ l₁ l₂ = (l₁ ∪ l₂ : list α) := rfl

@[simp] theorem zero_ndunion (s : multiset α) : ndunion 0 s = s :=
quot.induction_on s $ λ l, rfl

@[simp] theorem cons_ndunion (s t : multiset α) (a : α) :
  ndunion (a ::ₘ s) t = ndinsert a (ndunion s t) :=
quotient.induction_on₂ s t $ λ l₁ l₂, rfl

@[simp] theorem mem_ndunion {s t : multiset α} {a : α} : a ∈ ndunion s t ↔ a ∈ s ∨ a ∈ t :=
quotient.induction_on₂ s t $ λ l₁ l₂, list.mem_union

theorem le_ndunion_right (s t : multiset α) : t ≤ ndunion s t :=
quotient.induction_on₂ s t $ λ l₁ l₂, (suffix_union_right _ _).sublist.subperm

theorem subset_ndunion_right (s t : multiset α) : t ⊆ ndunion s t :=
subset_of_le (le_ndunion_right s t)

theorem ndunion_le_add (s t : multiset α) : ndunion s t ≤ s + t :=
quotient.induction_on₂ s t $ λ l₁ l₂, (union_sublist_append _ _).subperm

theorem ndunion_le {s t u : multiset α} : ndunion s t ≤ u ↔ s ⊆ u ∧ t ≤ u :=
multiset.induction_on s (by simp)
  (by simp [ndinsert_le, and_comm, and.left_comm] {contextual := tt})

theorem subset_ndunion_left (s t : multiset α) : s ⊆ ndunion s t :=
λ a h, mem_ndunion.2 $ or.inl h

theorem le_ndunion_left {s} (t : multiset α) (d : nodup s) : s ≤ ndunion s t :=
(le_iff_subset d).2 $ subset_ndunion_left _ _

theorem ndunion_le_union (s t : multiset α) : ndunion s t ≤ s ∪ t :=
ndunion_le.2 ⟨subset_of_le (le_union_left _ _), le_union_right _ _⟩

lemma nodup.ndunion (s : multiset α) {t : multiset α} : nodup t → nodup (ndunion s t) :=
quotient.induction_on₂ s t $ λ l₁ l₂, list.nodup.union _

@[simp, priority 980]
theorem ndunion_eq_union {s t : multiset α} (d : nodup s) : ndunion s t = s ∪ t :=
le_antisymm (ndunion_le_union _ _) $ union_le (le_ndunion_left _ d) (le_ndunion_right _ _)

theorem dedup_add (s t : multiset α) : dedup (s + t) = ndunion s (dedup t) :=
quotient.induction_on₂ s t $ λ l₁ l₂, congr_arg coe $ dedup_append _ _

/-! ### finset inter -/

/-- `ndinter s t` is the lift of the list `∩` operation. This operation
  does not respect multiplicities, unlike `s ∩ t`, but it is suitable as
  an intersection operation on `finset`. (`s ∩ t` would also work as a union operation
  on finset, but this is more efficient.) -/
def ndinter (s t : multiset α) : multiset α := filter (∈ t) s

@[simp] theorem coe_ndinter (l₁ l₂ : list α) : @ndinter α _ l₁ l₂ = (l₁ ∩ l₂ : list α) := rfl

@[simp] theorem zero_ndinter (s : multiset α) : ndinter 0 s = 0 := rfl

@[simp, priority 980]
theorem cons_ndinter_of_mem {a : α} (s : multiset α) {t : multiset α} (h : a ∈ t) :
  ndinter (a ::ₘ s) t = a ::ₘ (ndinter s t) := by simp [ndinter, h]

@[simp, priority 980]
theorem ndinter_cons_of_not_mem {a : α} (s : multiset α) {t : multiset α} (h : a ∉ t) :
  ndinter (a ::ₘ s) t = ndinter s t := by simp [ndinter, h]

@[simp] theorem mem_ndinter {s t : multiset α} {a : α} : a ∈ ndinter s t ↔ a ∈ s ∧ a ∈ t :=
mem_filter

@[simp]
lemma nodup.ndinter {s : multiset α} (t : multiset α) : nodup s → nodup (ndinter s t) :=
nodup.filter _

theorem le_ndinter {s t u : multiset α} : s ≤ ndinter t u ↔ s ≤ t ∧ s ⊆ u :=
by simp [ndinter, le_filter, subset_iff]

theorem ndinter_subset_left (s t : multiset α) : ndinter s t ⊆ s :=
subset_of_le (ndinter_le_left s t)

theorem ndinter_subset_right (s t : multiset α) : ndinter s t ⊆ t :=
(le_ndinter.1 le_rfl).2

theorem ndinter_le_right {s} (t : multiset α) (d : nodup s) : ndinter s t ≤ t :=
(le_iff_subset $ d.ndinter _).2 $ ndinter_subset_right _ _

theorem inter_le_ndinter (s t : multiset α) : s ∩ t ≤ ndinter s t :=
le_ndinter.2 ⟨inter_le_left _ _, subset_of_le $ inter_le_right _ _⟩

@[simp, priority 980]
theorem ndinter_eq_inter {s t : multiset α} (d : nodup s) : ndinter s t = s ∩ t :=
le_antisymm (le_inter (ndinter_le_left _ _) (ndinter_le_right _ d)) (inter_le_ndinter _ _)

theorem ndinter_eq_zero_iff_disjoint {s t : multiset α} : ndinter s t = 0 ↔ disjoint s t :=
by rw ← subset_zero; simp [subset_iff, disjoint]

end multiset

-- Assert that we define `finset` without the material on the set lattice.
-- Note that we cannot put this in `data.finset.basic` because we proved relevant lemmas there.
assert_not_exists set.sInter


variable (A B : Prop)

example : ((A → B) → A) → A := by
  intro (h : (A → B) → A)
  apply Or.elim (Classical.em A)
  intro (a : A)
  exact a
  intro (n : ¬ A)
  have (f : A → B) := fun (a : A) => False.elim (n a)
  exact h f

example : (A → B) → A := sorry -- this is not provable


/-
Copyright (c) 2019 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import data.dfinsupp.basic
import group_theory.submonoid.operations
import group_theory.subgroup.basic
/-!
# Direct sum

This file defines the direct sum of abelian groups, indexed by a discrete type.

## Notation

`⨁ i, β i` is the n-ary direct sum `direct_sum`.
This notation is in the `direct_sum` locale, accessible after `open_locale direct_sum`.

## References

* https://en.wikipedia.org/wiki/Direct_sum
-/

open_locale big_operators

universes u v w u₁

variables (ι : Type v) [dec_ι : decidable_eq ι] (β : ι → Type w)

/-- `direct_sum β` is the direct sum of a family of additive commutative monoids `β i`.

Note: `open_locale direct_sum` will enable the notation `⨁ i, β i` for `direct_sum β`. -/
@[derive [add_comm_monoid, inhabited]]
def direct_sum [Π i, add_comm_monoid (β i)] : Type* := Π₀ i, β i

instance [Π i, add_comm_monoid (β i)] : has_coe_to_fun (direct_sum ι β) (λ _, Π i : ι, β i) :=
dfinsupp.has_coe_to_fun

localized "notation `⨁` binders `, ` r:(scoped f, direct_sum _ f) := r" in direct_sum

namespace direct_sum

variables {ι}

section add_comm_group

variables [Π i, add_comm_group (β i)]

instance : add_comm_group (direct_sum ι β) := dfinsupp.add_comm_group

variables {β}
@[simp] lemma sub_apply (g₁ g₂ : ⨁ i, β i) (i : ι) : (g₁ - g₂) i = g₁ i - g₂ i :=
dfinsupp.sub_apply _ _ _

end add_comm_group

variables [Π i, add_comm_monoid (β i)]

@[simp] lemma zero_apply (i : ι) : (0 : ⨁ i, β i) i = 0 := rfl

variables {β}
@[simp] lemma add_apply (g₁ g₂ : ⨁ i, β i) (i : ι) : (g₁ + g₂) i = g₁ i + g₂ i :=
dfinsupp.add_apply _ _ _

variables (β)
include dec_ι

/-- `mk β s x` is the element of `⨁ i, β i` that is zero outside `s`
and has coefficient `x i` for `i` in `s`. -/
def mk (s : finset ι) : (Π i : (↑s : set ι), β i.1) →+ ⨁ i, β i :=
{ to_fun := dfinsupp.mk s,
  map_add' := λ _ _, dfinsupp.mk_add,
  map_zero' := dfinsupp.mk_zero, }

/-- `of i` is the natural inclusion map from `β i` to `⨁ i, β i`. -/
def of (i : ι) : β i →+ ⨁ i, β i :=
dfinsupp.single_add_hom β i

@[simp] lemma of_eq_same (i : ι) (x : β i) : (of _ i x) i = x :=
dfinsupp.single_eq_same

lemma of_eq_of_ne (i j : ι) (x : β i) (h : i ≠ j) : (of _ i x) j = 0 :=
dfinsupp.single_eq_of_ne h

@[simp] lemma support_zero [Π (i : ι) (x : β i), decidable (x ≠ 0)] :
  (0 : ⨁ i, β i).support = ∅ := dfinsupp.support_zero

@[simp] lemma support_of [Π (i : ι) (x : β i), decidable (x ≠ 0)]
  (i : ι) (x : β i) (h : x ≠ 0) :
  (of _ i x).support = {i} := dfinsupp.support_single_ne_zero h

lemma support_of_subset [Π (i : ι) (x : β i), decidable (x ≠ 0)] {i : ι} {b : β i} :
  (of _ i b).support ⊆ {i} := dfinsupp.support_single_subset

lemma sum_support_of [Π (i : ι) (x : β i), decidable (x ≠ 0)] (x : ⨁ i, β i) :
  ∑ i in x.support, of β i (x i) = x := dfinsupp.sum_single

variables {β}

theorem mk_injective (s : finset ι) : function.injective (mk β s) :=
dfinsupp.mk_injective s

theorem of_injective (i : ι) : function.injective (of β i) :=
dfinsupp.single_injective

@[elab_as_eliminator]
protected theorem induction_on {C : (⨁ i, β i) → Prop}
  (x : ⨁ i, β i) (H_zero : C 0)
  (H_basic : ∀ (i : ι) (x : β i), C (of β i x))
  (H_plus : ∀ x y, C x → C y → C (x + y)) : C x :=
begin
  apply dfinsupp.induction x H_zero,
  intros i b f h1 h2 ih,
  solve_by_elim
end

/-- If two additive homomorphisms from `⨁ i, β i` are equal on each `of β i y`,
then they are equal. -/
lemma add_hom_ext {γ : Type*} [add_monoid γ] ⦃f g : (⨁ i, β i) →+ γ⦄
  (H : ∀ (i : ι) (y : β i), f (of _ i y) = g (of _ i y)) : f = g :=
dfinsupp.add_hom_ext H

/-- If two additive homomorphisms from `⨁ i, β i` are equal on each `of β i y`,
then they are equal.

See note [partially-applied ext lemmas]. -/
@[ext] lemma add_hom_ext' {γ : Type*} [add_monoid γ] ⦃f g : (⨁ i, β i) →+ γ⦄
  (H : ∀ (i : ι), f.comp (of _ i) = g.comp (of _ i)) : f = g :=
add_hom_ext $ λ i, add_monoid_hom.congr_fun $ H i

variables {γ : Type u₁} [add_comm_monoid γ]

section to_add_monoid

variables (φ : Π i, β i →+ γ) (ψ : (⨁ i, β i) →+ γ)

/-- `to_add_monoid φ` is the natural homomorphism from `⨁ i, β i` to `γ`
induced by a family `φ` of homomorphisms `β i → γ`. -/
def to_add_monoid : (⨁ i, β i) →+ γ :=
(dfinsupp.lift_add_hom φ)

@[simp] lemma to_add_monoid_of (i) (x : β i) : to_add_monoid φ (of β i x) = φ i x :=
dfinsupp.lift_add_hom_apply_single φ i x

theorem to_add_monoid.unique (f : ⨁ i, β i) :
  ψ f = to_add_monoid (λ i, ψ.comp (of β i)) f :=
by {congr, ext, simp [to_add_monoid, of]}

end to_add_monoid

section from_add_monoid

/-- `from_add_monoid φ` is the natural homomorphism from `γ` to `⨁ i, β i`
induced by a family `φ` of homomorphisms `γ → β i`.

Note that this is not an isomorphism. Not every homomorphism `γ →+ ⨁ i, β i` arises in this way. -/
def from_add_monoid : (⨁ i, γ →+ β i) →+ (γ →+ ⨁ i, β i) :=
to_add_monoid $ λ i, add_monoid_hom.comp_hom (of β i)

@[simp] lemma from_add_monoid_of (i : ι) (f : γ →+ β i) :
  from_add_monoid (of _ i f) = (of _ i).comp f :=
by { rw [from_add_monoid, to_add_monoid_of], refl }

lemma from_add_monoid_of_apply (i : ι) (f : γ →+ β i) (x : γ) :
  from_add_monoid (of _ i f) x = of _ i (f x) :=
by rw [from_add_monoid_of, add_monoid_hom.coe_comp]

end from_add_monoid

variables (β)
/-- `set_to_set β S T h` is the natural homomorphism `⨁ (i : S), β i → ⨁ (i : T), β i`,
where `h : S ⊆ T`. -/
-- TODO: generalize this to remove the assumption `S ⊆ T`.
def set_to_set (S T : set ι) (H : S ⊆ T) :
  (⨁ (i : S), β i) →+ (⨁ (i : T), β i) :=
to_add_monoid $ λ i, of (λ (i : subtype T), β i) ⟨↑i, H i.prop⟩
variables {β}

omit dec_ι

/-- A direct sum over an empty type is trivial. -/
instance [is_empty ι] : unique (⨁ i, β i) := dfinsupp.unique

/-- The natural equivalence between `⨁ _ : ι, M` and `M` when `unique ι`. -/
protected def id (M : Type v) (ι : Type* := punit) [add_comm_monoid M] [unique ι] :
  (⨁ (_ : ι), M) ≃+ M :=
{ to_fun := direct_sum.to_add_monoid (λ _, add_monoid_hom.id M),
  inv_fun := of (λ _, M) default,
  left_inv := λ x, direct_sum.induction_on x
    (by rw [add_monoid_hom.map_zero, add_monoid_hom.map_zero])
    (λ p x, by rw [unique.default_eq p, to_add_monoid_of]; refl)
    (λ x y ihx ihy, by rw [add_monoid_hom.map_add, add_monoid_hom.map_add, ihx, ihy]),
  right_inv := λ x, to_add_monoid_of _ _ _,
  ..direct_sum.to_add_monoid (λ _, add_monoid_hom.id M) }

section congr_left
variables {κ : Type*}

/--Reindexing terms of a direct sum.-/
def equiv_congr_left (h : ι ≃ κ) : (⨁ i, β i) ≃+ ⨁ k, β (h.symm k) :=
{ map_add' := dfinsupp.comap_domain'_add _ _,
  ..dfinsupp.equiv_congr_left h }

@[simp] lemma equiv_congr_left_apply (h : ι ≃ κ) (f : ⨁ i, β i) (k : κ) :
  equiv_congr_left h f k = f (h.symm k) := dfinsupp.comap_domain'_apply _ _ _ _

end congr_left

section option
variables {α : option ι → Type w} [Π i, add_comm_monoid (α i)]
include dec_ι

/--Isomorphism obtained by separating the term of index `none` of a direct sum over `option ι`.-/
@[simps] noncomputable def add_equiv_prod_direct_sum : (⨁ i, α i) ≃+ α none × ⨁ i, α (some i) :=
{ map_add' := dfinsupp.equiv_prod_dfinsupp_add, ..dfinsupp.equiv_prod_dfinsupp }

end option

section sigma
variables {α : ι → Type u} {δ : Π i, α i → Type w} [Π i j, add_comm_monoid (δ i j)]

/--The natural map between `⨁ (i : Σ i, α i), δ i.1 i.2` and `⨁ i (j : α i), δ i j`.-/
noncomputable def sigma_curry : (⨁ (i : Σ i, _), δ i.1 i.2) →+ ⨁ i j, δ i j :=
{ to_fun := @dfinsupp.sigma_curry _ _ δ _,
  map_zero' := dfinsupp.sigma_curry_zero,
  map_add' := λ f g, dfinsupp.sigma_curry_add f g }

@[simp] lemma sigma_curry_apply (f : ⨁ (i : Σ i, _), δ i.1 i.2) (i : ι) (j : α i) :
  sigma_curry f i j = f ⟨i, j⟩ := dfinsupp.sigma_curry_apply f i j

/--The natural map between `⨁ i (j : α i), δ i j` and `Π₀ (i : Σ i, α i), δ i.1 i.2`, inverse of
`curry`.-/
noncomputable def sigma_uncurry : (⨁ i j, δ i j) →+ ⨁ (i : Σ i, _), δ i.1 i.2 :=
{ to_fun := dfinsupp.sigma_uncurry,
  map_zero' := dfinsupp.sigma_uncurry_zero,
  map_add' := dfinsupp.sigma_uncurry_add }

@[simp] lemma sigma_uncurry_apply (f : ⨁ i j, δ i j) (i : ι) (j : α i) :
  sigma_uncurry f ⟨i, j⟩ = f i j := dfinsupp.sigma_uncurry_apply f i j

/--The natural map between `⨁ (i : Σ i, α i), δ i.1 i.2` and `⨁ i (j : α i), δ i j`.-/
noncomputable def sigma_curry_equiv : (⨁ (i : Σ i, _), δ i.1 i.2) ≃+ ⨁ i j, δ i j :=
{ ..sigma_curry, ..dfinsupp.sigma_curry_equiv }

end sigma

/-- The canonical embedding from `⨁ i, A i` to `M` where `A` is a collection of `add_submonoid M`
indexed by `ι`.

When `S = submodule _ M`, this is available as a `linear_map`, `direct_sum.coe_linear_map`. -/
protected def coe_add_monoid_hom {M S : Type*} [decidable_eq ι] [add_comm_monoid M]
  [set_like S M] [add_submonoid_class S M] (A : ι → S) : (⨁ i, A i) →+ M :=
to_add_monoid (λ i, add_submonoid_class.subtype (A i))

@[simp] lemma coe_add_monoid_hom_of {M S : Type*} [decidable_eq ι] [add_comm_monoid M]
  [set_like S M] [add_submonoid_class S M] (A : ι → S) (i : ι) (x : A i) :
  direct_sum.coe_add_monoid_hom A (of (λ i, A i) i x) = x :=
to_add_monoid_of _ _ _

lemma coe_of_apply {M S : Type*} [decidable_eq ι] [add_comm_monoid M]
  [set_like S M] [add_submonoid_class S M] {A : ι → S} (i j : ι) (x : A i) :
  (of _ i x j : M) = if i = j then x else 0 :=
begin
  obtain rfl | h := decidable.eq_or_ne i j,
  { rw [direct_sum.of_eq_same, if_pos rfl], },
  { rw [direct_sum.of_eq_of_ne _ _ _ _ h, if_neg h, add_submonoid_class.coe_zero], },
end

/-- The `direct_sum` formed by a collection of additive submonoids (or subgroups, or submodules) of
`M` is said to be internal if the canonical map `(⨁ i, A i) →+ M` is bijective.

For the alternate statement in terms of independence and spanning, see
`direct_sum.subgroup_is_internal_iff_independent_and_supr_eq_top` and
`direct_sum.is_internal_submodule_iff_independent_and_supr_eq_top`. -/
def is_internal {M S : Type*} [decidable_eq ι] [add_comm_monoid M]
  [set_like S M] [add_submonoid_class S M] (A : ι → S) : Prop :=
function.bijective (direct_sum.coe_add_monoid_hom A)

lemma is_internal.add_submonoid_supr_eq_top {M : Type*} [decidable_eq ι] [add_comm_monoid M]
  (A : ι → add_submonoid M)
  (h : is_internal A) : supr A = ⊤ :=
begin
  rw [add_submonoid.supr_eq_mrange_dfinsupp_sum_add_hom, add_monoid_hom.mrange_top_iff_surjective],
  exact function.bijective.surjective h,
end

end direct_sum


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

import linear_algebra.tensor_product
import linear_algebra.direct_sum_module

/-!
# Tensor products of direct sums

This file shows that taking `tensor_product`s commutes with taking `direct_sum`s in both arguments.
-/

section ring

namespace tensor_product

open_locale tensor_product
open_locale direct_sum
open linear_map

variables (R : Type*) [comm_ring R]
variables (ι₁ : Type*) (ι₂ : Type*)
variables [decidable_eq ι₁] [decidable_eq ι₂]
variables (M₁ : ι₁ → Type*) (M₂ : ι₂ → Type*)
variables [Π i₁, add_comm_group (M₁ i₁)] [Π i₂, add_comm_group (M₂ i₂)]
variables [Π i₁, module R (M₁ i₁)] [Π i₂, module R (M₂ i₂)]

/-- The linear equivalence `(⨁ i₁, M₁ i₁) ⊗ (⨁ i₂, M₂ i₂) ≃ (⨁ i₁, ⨁ i₂, M₁ i₁ ⊗ M₂ i₂)`, i.e.
"tensor product distributes over direct sum". -/
def direct_sum :
  (⨁ i₁, M₁ i₁) ⊗[R] (⨁ i₂, M₂ i₂) ≃ₗ[R] (⨁ (i : ι₁ × ι₂), M₁ i.1 ⊗[R] M₂ i.2) :=
begin
  refine linear_equiv.of_linear
    (lift $ direct_sum.to_module R _ _ $ λ i₁, flip $ direct_sum.to_module R _ _ $ λ i₂,
      flip $ curry $ direct_sum.lof R (ι₁ × ι₂) (λ i, M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂))
    (direct_sum.to_module R _ _ $ λ i, map (direct_sum.lof R _ _ _) (direct_sum.lof R _ _ _))
    (linear_map.ext $ direct_sum.to_module.ext _ $ λ i, mk_compr₂_inj $
      linear_map.ext $ λ x₁, linear_map.ext $ λ x₂, _)
    (mk_compr₂_inj $ linear_map.ext $ direct_sum.to_module.ext _ $ λ i₁, linear_map.ext $ λ x₁,
      linear_map.ext $ direct_sum.to_module.ext _ $ λ i₂, linear_map.ext $ λ x₂, _),
  repeat { rw compr₂_apply <|> rw comp_apply <|> rw id_apply <|> rw mk_apply <|>
    rw direct_sum.to_module_lof <|> rw map_tmul <|> rw lift.tmul <|> rw flip_apply <|>
    rw curry_apply },
  cases i; refl
end

@[simp] theorem direct_sum_lof_tmul_lof (i₁ : ι₁) (m₁ : M₁ i₁) (i₂ : ι₂) (m₂ : M₂ i₂) :
  direct_sum R ι₁ ι₂ M₁ M₂ (direct_sum.lof R ι₁ M₁ i₁ m₁ ⊗ₜ direct_sum.lof R ι₂ M₂ i₂ m₂) =
  direct_sum.lof R (ι₁ × ι₂) (λ i, M₁ i.1 ⊗[R] M₂ i.2) (i₁, i₂) (m₁ ⊗ₜ m₂) :=
by simp [direct_sum]

end tensor_product

end ring


/- This is a short tutorial on lean for the Logic in Computer Science course at the university
of Ljubljana. -/

namespace logika_v_racunalnistvu

/- Definitions of inductive types are made using the inductive keyword. Different constructors
   are separated by |. -/

inductive list (A : Type) : Type
| nil : list
| cons : A → list → list

/- We open the namespace list, so that we can use nil and cons directly. -/

namespace list

/- We will now define some basic operations on lists. -/

/- Direct definitions are made using the definition keyword, followed by := -/

definition unit {A : Type} (a : A) : list A :=
cons a nil

/- A shorthand for definition is def, which may also be used. -/

/- Since the type of lists is an inductive type, we can make inductive definitions on list
using pattern matching. The syntax is analogous to the syntax of the inductive type itself.
Note that in pattern matching definitions, we don't use := at the end of the specification. -/

def fold {A : Type} {B : Type} (b : B) (μ : A → B → B) : list A → B
| nil := b
| (cons a l) := μ a (fold l)

def map {A : Type} {B : Type} (f : A → B) : list A → list B  :=
fold nil (cons ∘ f)

def length {A : Type} : list A → ℕ :=
fold 0 (λ _ n, n + 1)

/- Exercise 1: -/
def sum_list_ℕ : list ℕ → ℕ :=
fold 0 (+)

/- Tests for sum_list_ℕ: -/
#reduce sum_list_ℕ nil
#reduce sum_list_ℕ (cons 1 (cons 2 (cons 3 nil)))

def concat {A : Type} : list A → list A → list A :=
fold id (λ a f l, cons a (f l))

#reduce concat (cons 1 nil) (nil)

/- The idea of the flattening operation is to transform a list of lists into an ordinary list
   by iterated concatenation. For example, we should have

   flatten (cons (cons 1 (cons 2 nil)) (cons (cons 3 nil) (cons 4 (cons 5 nil))))

   should evaluate to

   cons 1 (cons 2 (cons 3 (cons 4 (cons 5 nil))))
   -/
/- Exercise 2: (Can it be done just by fold? Better safe than sorry.) -/
def flatten {A : Type} : list (list A) → list A
| nil := nil
| (cons hd tl) := concat hd (flatten tl)

/- We have now finished defining our basic operations on lists. Let us check by some examples
   that the operations indeed do what they are supposed to do. With your mouse, hover over the
   #reduce keyword to see what each term reduces to. -/

#reduce concat (cons 1 (cons 2 (cons 3 nil))) (cons 4 (cons 5 nil))

#reduce flatten (cons (cons 1 (cons 2 nil)) (cons (cons 3 nil) (cons (cons 4 (cons 5 nil)) nil)))

/- Of course, if you really want to know that your operations behave as expected, you should
   prove the relevant properties about them. This is what we will do next. -/

/- When proving theorems, we can also proceed by pattern matching. In a pattern matching argument
   we can recursively call the object we are defining on earlier instances.

   The arguments that we want to pattern-match on, must appear after the colon (:) in the
   specification of the theorem. -/

theorem map_id {A : Type} :
    ∀ (x : list A), map id x = x
| nil := rfl
| (cons a x) :=
    calc
    map id (cons a x)
        = cons a (map id x) : rfl
    ... = cons a x : by rw map_id

theorem map_composition {A : Type} {B : Type} {C : Type} (f : A → B) (g : B → C) :
    ∀ (x : list A), map (g ∘ f) x = map g (map f x)
| nil := rfl
| (cons a x) :=
    calc
    map (g ∘ f) (cons a x)
        = cons (g (f a)) (map (g ∘ f) x) : rfl
    ... = cons (g (f a)) (map g (map f x)) : by rw map_composition
    ... = map g (map f (cons a x)) : rfl

/- Next, we prove some properties concatenation. Concatenation of lists is an associative
   operation, and it satisfies the left and right unit laws.universe

   In order to prove associativity, we note that since concatenation is defined by induction
   on the left argument, we will again use induction on the left argument to prove this
   propoerty. The proof is presented by pattern matching.

   In the proof we will use the built-in equation compiler. We just calculate as if we were
   working on a sheet of paper, and each time we mention the reason why the equality holds. -/

theorem assoc_concat {A : Type} :
    ∀ (x y z : list A), concat (concat x y) z = concat x (concat y z)
| nil _ _ := rfl
| (cons a l) y z :=
    calc
    concat (concat (cons a l) y) z
        = cons a (concat (concat l y) z) : by reflexivity
    ... = cons a (concat l (concat y z)) : by rw assoc_concat
    ... = concat (cons a l) (concat y z) : by reflexivity

theorem left_unit_concat {A : Type} :
    ∀ (x : list A), concat nil x = x :=
    eq.refl

theorem right_unit_concat {A : Type} :
    ∀ (x : list A), concat x nil = x
| nil := rfl
| (cons a x) :=
    show cons a (concat x nil) = cons a x,
    by rw right_unit_concat

/- Next, we prove the elementary properties of the length function. -/

theorem length_nil {A : Type} :
    length (@nil A) = 0 := rfl

theorem length_unit {A : Type} (a : A) :
    length (unit a) = 1 := rfl

theorem length_concat {A : Type} :
    ∀ (x y : list A), length (concat x y) = length x + length y
| nil y :=
    calc
    length (concat nil y)
        = length y : rfl
    ... = 0 + length y : by rw nat.zero_add
    ... = length nil + length y : by rw length_nil
| (cons a x) y :=
    calc
    length (concat (cons a x) y)
        = length (concat x y) + 1 : rfl
    ... = (length x + length y) + 1 : by rw length_concat
    ... = (length x + 1) + length y : by rw nat.succ_add
    ... = (length (cons a x)) + length y : rfl

/- Next, we prove the elemenatary properties of the flatten function. -/

/- Exercise 3: -/
theorem flatten_unit {A : Type} :
    ∀ (x : list A), flatten (unit x) = x
| nil := rfl
| (cons hd tl) :=
    calc
    flatten (unit (cons hd tl))
        = (cons hd (flatten (unit tl))) : rfl
    ... = (cons hd tl) : by rw flatten_unit
    ... = (cons hd tl) : rfl

/- Exercise 4: -/
theorem flatten_map_unit {A : Type} :
    ∀  (x : list A), flatten (map unit x) = x
| nil := rfl
| (cons hd tl) :=
    calc
    flatten (map unit (cons hd tl))
        = flatten (cons (unit hd) (map unit tl)) : rfl
    ... = cons hd (flatten (map unit tl)) : rfl
    ... = cons hd tl : by rw flatten_map_unit
    ... = (cons hd tl) : rfl

/- Exercise 5: -/
theorem length_flatten {A : Type} :
    ∀ (x : list (list A)), length (flatten x) = sum_list_ℕ (map length x)
| nil := rfl
| (cons hd tl) :=
    calc
    length (flatten (cons hd tl))
        = length (concat hd (flatten tl)) : rfl
    ... = (length hd) + (length (flatten tl)) : by rw length_concat
    ... = (length hd) + (sum_list_ℕ (map length tl)) : by rw length_flatten
    ... = sum_list_ℕ (map length (cons hd tl)) : rfl

/- Exercise 6: -/
theorem flatten_concat {A : Type} :
    ∀ (x y : list (list A)), flatten (concat x y) = concat (flatten x) (flatten y)
| nil _ := rfl
| (cons hd tl) y :=
    calc
    flatten (concat (cons hd tl) y)
        = flatten (cons hd (concat tl y)) : rfl
    ... = concat hd (flatten (concat tl y)) : rfl
    ... = concat hd (concat (flatten tl) (flatten y)) : by rw flatten_concat
    ... = concat (concat hd (flatten tl)) (flatten y) : by rw assoc_concat
    ... = concat (flatten (cons hd tl)) (flatten y) : rfl

/- Exercise 7: -/
theorem flatten_flatten {A : Type} :
    ∀ (x : list (list (list A))), flatten (flatten x) = flatten (map flatten x)
| nil := rfl
| (cons hd tl) :=
    calc
    flatten (flatten (cons hd tl))
        = flatten (concat hd (flatten tl)) : rfl
    ... = concat (flatten hd) (flatten (flatten tl)) : by rw flatten_concat
    ... = concat (flatten hd) (flatten (map flatten tl)) : by rw flatten_flatten
    ... = flatten (map flatten (cons hd tl)) : rfl

/- This concludes our coverage of lists in Lean. -/

end list

/- Next, we study lists of a fixed length. They are a natural example of a dependent type.-/

inductive vector (A : Type) : ℕ → Type
| nil : vector 0
| cons : ∀ {n : ℕ}, A → vector n → vector (n+1)

namespace vector

def map {A : Type} {B : Type} (f : A → B) :
    ∀ {n : ℕ}, vector A n → vector B n
| 0 nil := nil
| (n+1) (cons a x) := cons (f a) (map x)

def concat {A : Type} :
    ∀ {m n : ℕ}, vector A m → vector A n → vector A (m+n)
| 0 n nil y :=
    begin
    rw nat.zero_add,
    exact y,
    end
| (m+1) n (cons a x) y :=
    begin
    rw nat.succ_add,
    exact cons a (concat x y),
    end

def head {A : Type} :
    ∀ {n : ℕ}, vector A (n+1) → A
| n (cons a x) := a

def tail {A : Type} :
    ∀ {n : ℕ}, vector A (n+1) → vector A n
| n (cons a x) := x

/- Using lists of fixed length, we can define matrices. The type
   Matrix m n A is the type of matrices with m rows and n columns
   and with coefficients in A. -/

def Matrix (m n : ℕ) (A : Type) : Type :=
vector (vector A n) m

def top_row {A : Type} {m n : ℕ} :
    Matrix (m+1) n A → vector A n :=
head

def tail_vertical {A : Type} {m n : ℕ} :
    Matrix (m+1) n A → Matrix m n A :=
tail

def left_column {A : Type} {m n : ℕ} :
    Matrix m (n+1) A → vector A m :=
map head

def tail_horizontal {A : Type} {m n : ℕ} :
    Matrix m (n+1) A → Matrix m n A :=
map tail

/- Since matrices are rectangular, we have a horizontal as well as vertical empty matrices. -/

def nil_vertical {A : Type} {n : ℕ} : Matrix 0 n A := nil

theorem eq_nil_vertical {A : Type} :
    ∀ {n : ℕ} (x : Matrix 0 n A), x = nil_vertical
| 0 nil := rfl
| (n+1) nil := rfl

def nil_horizontal {A : Type} : ∀ {m : ℕ}, Matrix m 0 A
| 0 := nil
| (m+1) := cons nil nil_horizontal

theorem eq_nil_horizontal {A : Type} :
    ∀ {m : ℕ} (x : Matrix m 0 A), x = nil_horizontal
| 0 nil := rfl
| (m+1) (cons nil M) :=
    calc
    cons nil M
        = cons nil nil_horizontal : by rw eq_nil_horizontal M
    ... = nil_horizontal : rfl

/- Similarly, there is a horizontal cons and a vertical cons. -/

/- cons_vertical adds a new row from the top. -/

def cons_vertical {A : Type} {m n : ℕ} :
    vector A n → Matrix m n A → Matrix (m+1) n A :=
cons

/- cons_horizontal adds a new column from the left. -/

def cons_horizontal {A : Type} :
    ∀ {m n : ℕ}, vector A m → Matrix m n A → Matrix m (n+1) A
| 0 n nil M := nil
| (m+1) n (cons a x) M :=
    cons (cons a (top_row M)) (cons_horizontal x (tail_vertical M))

/- We define the transposition of a matrix. -/

def transpose {A : Type} :
    ∀ {m n : ℕ}, Matrix m n A → Matrix n m A
| 0 n M := nil_horizontal
| (m+1) n (cons x M) := cons_horizontal x (transpose M)

/- The following two theorems show how transpose interacts with the basic operations on
   matrices. These will help to show that transposition is an involution. -/

/- Exercise 9: (moved here because trivial and I want to use it in Ex. 8)-/
theorem transpose_cons_vertical {A : Type} :
    ∀ {m n : ℕ} (x : vector A n) (M : Matrix m n A),
    transpose (cons_vertical x M) = cons_horizontal x (transpose M)
| m 0 _ _ := rfl
| _ n _ _ := rfl

/- Exercise 8: -/
theorem transpose_cons_horizontal {A : Type} :
    ∀ {m n : ℕ} (x : vector A m) (M : Matrix m n A),
    transpose (cons_horizontal x M) = cons_vertical x (transpose M)
| 0 0 nil nil := rfl
| 0 n nil M := rfl
| (m+1) n (cons a x) (cons v M) :=
    calc
    transpose (cons_horizontal (cons a x) (cons v M))
        = transpose (cons_vertical (cons a v) (cons_horizontal x M)) : rfl
    ... = cons_horizontal (cons a v) (transpose (cons_horizontal x M)) : by rw transpose_cons_vertical
    ... = cons_horizontal (cons a v) (cons_vertical x (transpose M)) : by rw transpose_cons_horizontal
    ... = cons_horizontal (cons a v) (cons x (transpose M)) : rfl
    ... = cons (cons a (top_row (cons x (transpose M)))) (cons_horizontal v (tail_vertical (cons x (transpose M)))) : rfl
    ... = cons (cons a (top_row (cons_vertical x (transpose M)))) (cons_horizontal v (tail_vertical (cons_vertical x (transpose M)))) : rfl
    ... = cons (cons a x) (cons_horizontal v (transpose M)) : rfl
    ... = cons (cons a x) (transpose (cons_vertical v M)) : by rw transpose_cons_vertical
    ... = cons (cons a x) (transpose (cons v M)) : rfl
    ... = cons_vertical (cons a x) (transpose (cons v M)) : rfl
/- Note: A bunch of these rfl's can be commented out for the proof to still work. Of course I leave them
         in for legibility's sake, otherwise I'd be lost the next time I try to read this. -/

/- We finally show that transposition is an involution. -/

theorem transpose_transpose {A : Type} :
    ∀ {m n : ℕ} (M : Matrix m n A), transpose (transpose M) = M
| 0 0 nil := rfl
| 0 (n+1) nil := rfl
| m n (cons v M) :=
    calc
    transpose (transpose (cons v M))
        = transpose (transpose (cons_vertical v M)) : rfl
    ... = transpose (cons_horizontal v (transpose M)) : by rw transpose_cons_vertical
    ... = cons_vertical v (transpose (transpose M)) : by rw transpose_cons_horizontal
    ... = cons_vertical v M : by rw transpose_transpose
    ... = cons v M : rfl

end vector

end logika_v_racunalnistvu


import sub
import multiquotient
import relation.additional

namespace UA
  universes u_lang u_str

  section

    parameter [σ : signature.{u_lang}]
    include σ

    /- A `congruence` is an equiverlance relation which is 'respected' by the operations. This is
    -- precisely the condition required for the quotient set to be a well-defined structure.   -/

    section
      parameter {α : Type u_str}
      parameter [act : action_on α]

      -- the operations are said to 'respect' a binary relation ~ if,
      -- in an equation f (x, y, z) = a,
      -- altering an input x → x' where x ~ x'
      -- causes a similar alteration on the output
      def operations_respect_relation (r : α → α → Prop) :=
      ∀ (f : σ.F) (xxx yyy : vector α (arity_of f)),
      (∀ i, r (xxx.nth i) (yyy.nth i)) → r (act f xxx) (act f yyy)

      -- a binary relation on the structure A is respected by the operations
      -- if and only if it is closed as a subset of the product structure
      lemma relation_is_closed_iff_respected {r} :
      operations_respect_relation r ↔ closed (λ (p : α×α), r p.1 p.2) :=
      begin
        split,

        intros h f xyxyxy h_input,
        specialize h f (vector.map prod.fst xyxyxy) (vector.map prod.snd xyxyxy),
        rw set.range_subset_iff at h_input,
        simp at h,
        exact h h_input,

        intros h f xxx yyy h_input,
        specialize h f (vector.zip xxx yyy),
        rw set.range_subset_iff at h,
        simp at h,
        exact h h_input,
      end

      /- *** congruence generated by a binary relation *** -/

      inductive cong_gen (r : α → α → Prop) : α → α → Prop
      | rel   {} (  x y) : r x y → cong_gen x y
      | refl  {} (    x) : cong_gen x x
      | symm  {} (  x y) : cong_gen x y → cong_gen y x
      | trans {} (x y z) : cong_gen x y → cong_gen y z → cong_gen x z
      | op    {}     (f) (xxx : vector α _) (yyy : vector α _)
      : (∀ i, cong_gen (xxx.nth i) (yyy.nth i)) → cong_gen (act f xxx) (act f yyy)

      theorem cong_gen_is_equivalence {r} : equivalence (cong_gen r) :=
      mk_equivalence _ cong_gen.refl cong_gen.symm cong_gen.trans

      theorem cong_gen_is_respected {r} : operations_respect_relation (cong_gen r) :=
      begin
        rw relation_is_closed_iff_respected,
        intros f input h,
        rw set.subset_def at h,
        rw set.forall_range_iff at h,
        have lem := cong_gen.op f (vector.map prod.fst input) (vector.map prod.snd input),
        simp at lem,
        exact lem h,
      end

      /- *** alternative characterisation of congruence generated  ***-/

      def smallest_cong_ctg (r) (x y) :=
      ∀ s, operations_respect_relation s → equivalence s → s contains r → s x y

      lemma smallest_cong_ctg_is_respected {r} : operations_respect_relation (smallest_cong_ctg r) :=
      begin
        intros f xxx yyy h,
        intros s s_closed s_eqv s_contains,
        apply s_closed f xxx yyy,
        intro i,
        exact h i s s_closed s_eqv s_contains,
      end

      theorem smallest_cong_ctg_is_equivalence {r} : equivalence (smallest_cong_ctg r) :=
      begin
        rw equivalence,
        split,
        intros x s h_closed h_eqv h_contains,
        exact h_eqv.left x,

        split,
        intros x y h s h_closed h_eqv h_contains,
        specialize h s h_closed h_eqv h_contains,
        exact h_eqv.right.left h,

        intros x y z h_xy h_yz s h_closed h_eqv h_contains,
        specialize h_xy s h_closed h_eqv h_contains,
        specialize h_yz s h_closed h_eqv h_contains,
        exact h_eqv.right.right h_xy h_yz,
      end

      theorem cong_gen_is_smallest_containing {r x y} : (cong_gen r) x y ↔ (smallest_cong_ctg r) x y :=
      begin

        split,
        intro h,
        induction h,

        case cong_gen.rel   : a b               { intros _ _ _ h_contains, specialize h_contains a b, cc, },
        case cong_gen.refl  : a                 { exact (smallest_cong_ctg_is_equivalence).left a, },
        case cong_gen.symm  : a b   _   hab     { exact (smallest_cong_ctg_is_equivalence).right.left hab, },
        case cong_gen.trans : a b c _ _ hab hbc { exact (smallest_cong_ctg_is_equivalence).right.right hab hbc, },

        case cong_gen.op : f xxx yyy h i_hyp  {
          have r_respects_spefific_input :
          ((∀ i, smallest_cong_ctg r (xxx.nth i) (yyy.nth i)) → smallest_cong_ctg r (act f xxx) (act f yyy)),
          intro i, exact smallest_cong_ctg_is_respected f xxx yyy i,
          exact r_respects_spefific_input i_hyp,
        },

        intro h,
        exact h (cong_gen r) (cong_gen_is_respected) (cong_gen_is_equivalence) (cong_gen.rel),

      end
    end

    /- We implement `congruence` as a class (in the vein of setoid). -/

    class congruence (α : Type u_str) [act : action_on α] :=
    (s : setoid α) (closed : operations_respect_relation s.r)

    instance congruence_setoid {α : Type u_str} [act : action_on α]
    [cong : congruence α] : setoid α := cong.s

    namespace congruence
      section
        parameter {α : Type u_str}
        parameter [act : action_on α]
        parameter (self : congruence α)

        def r := self.s.r
        def π := quot.mk r

        theorem respectful {f} {{a b : vector α _}} :
        (∀ i, a.nth i ≈ b.nth i) → π (act f a) = π (act f b) :=
        begin
          intro h,
          apply quot.sound,
          apply congruence.closed,
          exact h,
        end

        instance quotient_action : action_on (quot r) :=
        λ f, mquotient.lift (π ∘ act f) (respectful)

        instance proj_is_hom : @homomorphism _ _ _ act _ π := λ _ _, by { apply mquotient.comp }

        def quotient : Structure.{u_lang u_str} := ⟨quot r, quotient_action⟩
        def proj : Homomorphism α quotient := ⟨π, proj_is_hom⟩

        @[simp] lemma action_of_quotient {{f}} {{p}} :
        quotient_action f p = (mquotient.lift (π ∘ act f) (respectful)) p := rfl

        include act
        def gen_by (r : α → α → Prop) : congruence α := ⟨setoid.mk (cong_gen r) (cong_gen_is_equivalence), cong_gen_is_respected⟩
        def gen_by_set (R : set (α × α)) : congruence α := gen_by (λ x y, (x, y) ∈ R)
        lemma gentd_contains_gens {R} {x y} : (x, y) ∈ R → (gen_by_set R).r x y := λ h, cong_gen.rel _ _ h
        omit act

      end
    end congruence

  end
end UA


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

! This file was ported from Lean 3 source module data.fintype.parity
! leanprover-community/mathlib commit 327c3c0d9232d80e250dc8f65e7835b82b266ea5
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fintype.Card
import Mathbin.Algebra.Parity

/-!
# The cardinality of `fin (bit0 n)` is even.

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

variable {α : Type _}

namespace Fintype

#print Fintype.IsSquare.decidablePred /-
instance IsSquare.decidablePred [Mul α] [Fintype α] [DecidableEq α] :
    DecidablePred (IsSquare : α → Prop) := fun a => Fintype.decidableExistsFintype
#align fintype.is_square.decidable_pred Fintype.IsSquare.decidablePred
-/

end Fintype

#print Fintype.card_fin_even /-
/-- The cardinality of `fin (bit0 n)` is even, `fact` version.
This `fact` is needed as an instance by `matrix.special_linear_group.has_neg`. -/
theorem Fintype.card_fin_even {n : ℕ} : Fact (Even (Fintype.card (Fin (bit0 n)))) :=
  ⟨by
    rw [Fintype.card_fin]
    exact even_bit0 _⟩
#align fintype.card_fin_even Fintype.card_fin_even
-/


lemma lt_aux_one (a b : mynat) : a ≤ b ∧ ¬ (b ≤ a) → succ a ≤ b :=
begin
intro h,
cases h with h1 h2,
cases h1 with c h1,
cases c,
rw add_zero at h1,
rw h1 at h2,
exfalso,
exact h2 (le_refl a),
rwa [h1, add_succ],
apply succ_le_succ,
use c,
refl,
end


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

! This file was ported from Lean 3 source module data.set.sups
! leanprover-community/mathlib commit 97eab48559068f3d6313da387714ef25768fb730
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.NAry
import Mathbin.Order.UpperLower.Basic

/-!
# Set family operations

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

This file defines a few binary operations on `set α` for use in set family combinatorics.

## Main declarations

* `s ⊻ t`: Set of elements of the form `a ⊔ b` where `a ∈ s`, `b ∈ t`.
* `s ⊼ t`: Set of elements of the form `a ⊓ b` where `a ∈ s`, `b ∈ t`.

## Notation

We define the following notation in locale `set_family`:
* `s ⊻ t`
* `s ⊼ t`

## References

[B. Bollobás, *Combinatorics*][bollobas1986]
-/

open Function

variable {α : Type _}

#print HasSups /-
/-- Notation typeclass for pointwise supremum `⊻`. -/
class HasSups (α : Type _) where
  sups : α → α → α
#align has_sups HasSups
-/

#print HasInfs /-
/-- Notation typeclass for pointwise infimum `⊼`. -/
class HasInfs (α : Type _) where
  infs : α → α → α
#align has_infs HasInfs
-/

-- mathport name: «expr ⊻ »
infixl:74
  " ⊻ " =>-- This notation is meant to have higher precedence than `⊔` and `⊓`, but still within the realm of
  -- other binary operations
  HasSups.sups

-- mathport name: «expr ⊼ »
infixl:75 " ⊼ " => HasInfs.infs

namespace Set

section Sups

variable [SemilatticeSup α] (s s₁ s₂ t t₁ t₂ u v : Set α)

#print Set.hasSups /-
/-- `s ⊻ t` is the set of elements of the form `a ⊔ b` where `a ∈ s`, `b ∈ t`. -/
protected def hasSups : HasSups (Set α) :=
  ⟨image2 (· ⊔ ·)⟩
#align set.has_sups Set.hasSups
-/

scoped[SetFamily] attribute [instance] Set.hasSups

variable {s s₁ s₂ t t₁ t₂ u} {a b c : α}

/- warning: set.mem_sups -> Set.mem_sups is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {c : α}, Iff (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)) (Exists.{succ u1} α (fun (a : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) => Exists.{succ u1} α (fun (b : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) => Eq.{succ u1} α (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b) c)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {c : α}, Iff (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) c (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)) (Exists.{succ u1} α (fun (a : α) => And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) (Exists.{succ u1} α (fun (b : α) => And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) (Eq.{succ u1} α (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b) c)))))
Case conversion may be inaccurate. Consider using '#align set.mem_sups Set.mem_supsₓ'. -/
@[simp]
theorem mem_sups : c ∈ s ⊻ t ↔ ∃ a ∈ s, ∃ b ∈ t, a ⊔ b = c := by simp [(· ⊻ ·)]
#align set.mem_sups Set.mem_sups

/- warning: set.sup_mem_sups -> Set.sup_mem_sups is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α} {b : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α} {b : α}, (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t))
Case conversion may be inaccurate. Consider using '#align set.sup_mem_sups Set.sup_mem_supsₓ'. -/
theorem sup_mem_sups : a ∈ s → b ∈ t → a ⊔ b ∈ s ⊻ t :=
  mem_image2_of_mem
#align set.sup_mem_sups Set.sup_mem_sups

#print Set.sups_subset /-
theorem sups_subset : s₁ ⊆ s₂ → t₁ ⊆ t₂ → s₁ ⊻ t₁ ⊆ s₂ ⊻ t₂ :=
  image2_subset
#align set.sups_subset Set.sups_subset
-/

#print Set.sups_subset_left /-
theorem sups_subset_left : t₁ ⊆ t₂ → s ⊻ t₁ ⊆ s ⊻ t₂ :=
  image2_subset_left
#align set.sups_subset_left Set.sups_subset_left
-/

#print Set.sups_subset_right /-
theorem sups_subset_right : s₁ ⊆ s₂ → s₁ ⊻ t ⊆ s₂ ⊻ t :=
  image2_subset_right
#align set.sups_subset_right Set.sups_subset_right
-/

/- warning: set.image_subset_sups_left -> Set.image_subset_sups_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {b : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.image.{u1, u1} α α (fun (a : α) => Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b) s) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {b : α}, (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.image.{u1, u1} α α (fun (a : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b) s) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t))
Case conversion may be inaccurate. Consider using '#align set.image_subset_sups_left Set.image_subset_sups_leftₓ'. -/
theorem image_subset_sups_left : b ∈ t → (fun a => a ⊔ b) '' s ⊆ s ⊻ t :=
  image_subset_image2_left
#align set.image_subset_sups_left Set.image_subset_sups_left

/- warning: set.image_subset_sups_right -> Set.image_subset_sups_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.image.{u1, u1} α α (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a) t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α}, (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.image.{u1, u1} α α ((fun (x._@.Mathlib.Data.Set.Sups._hyg.1635 : α) (x._@.Mathlib.Data.Set.Sups._hyg.1637 : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) x._@.Mathlib.Data.Set.Sups._hyg.1635 x._@.Mathlib.Data.Set.Sups._hyg.1637) a) t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t))
Case conversion may be inaccurate. Consider using '#align set.image_subset_sups_right Set.image_subset_sups_rightₓ'. -/
theorem image_subset_sups_right : a ∈ s → (· ⊔ ·) a '' t ⊆ s ⊻ t :=
  image_subset_image2_right
#align set.image_subset_sups_right Set.image_subset_sups_right

/- warning: set.forall_sups_iff -> Set.forall_sups_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {p : α -> Prop}, Iff (forall (c : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)) -> (p c)) (forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (forall (b : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (p (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {p : α -> Prop}, Iff (forall (c : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) c (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)) -> (p c)) (forall (a : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (forall (b : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (p (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b))))
Case conversion may be inaccurate. Consider using '#align set.forall_sups_iff Set.forall_sups_iffₓ'. -/
theorem forall_sups_iff {p : α → Prop} : (∀ c ∈ s ⊻ t, p c) ↔ ∀ a ∈ s, ∀ b ∈ t, p (a ⊔ b) :=
  forall_image2_iff
#align set.forall_sups_iff Set.forall_sups_iff

/- warning: set.sups_subset_iff -> Set.sups_subset_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {u : Set.{u1} α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t) u) (forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (forall (b : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b) u)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {u : Set.{u1} α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t) u) (forall (a : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (forall (b : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b) u)))
Case conversion may be inaccurate. Consider using '#align set.sups_subset_iff Set.sups_subset_iffₓ'. -/
@[simp]
theorem sups_subset_iff : s ⊻ t ⊆ u ↔ ∀ a ∈ s, ∀ b ∈ t, a ⊔ b ∈ u :=
  image2_subset_iff
#align set.sups_subset_iff Set.sups_subset_iff

#print Set.sups_nonempty /-
@[simp]
theorem sups_nonempty : (s ⊻ t).Nonempty ↔ s.Nonempty ∧ t.Nonempty :=
  image2_nonempty_iff
#align set.sups_nonempty Set.sups_nonempty
-/

#print Set.Nonempty.sups /-
protected theorem Nonempty.sups : s.Nonempty → t.Nonempty → (s ⊻ t).Nonempty :=
  Nonempty.image2
#align set.nonempty.sups Set.Nonempty.sups
-/

#print Set.Nonempty.of_sups_left /-
theorem Nonempty.of_sups_left : (s ⊻ t).Nonempty → s.Nonempty :=
  Nonempty.of_image2_left
#align set.nonempty.of_sups_left Set.Nonempty.of_sups_left
-/

#print Set.Nonempty.of_sups_right /-
theorem Nonempty.of_sups_right : (s ⊻ t).Nonempty → t.Nonempty :=
  Nonempty.of_image2_right
#align set.nonempty.of_sups_right Set.Nonempty.of_sups_right
-/

#print Set.empty_sups /-
@[simp]
theorem empty_sups : ∅ ⊻ t = ∅ :=
  image2_empty_left
#align set.empty_sups Set.empty_sups
-/

#print Set.sups_empty /-
@[simp]
theorem sups_empty : s ⊻ ∅ = ∅ :=
  image2_empty_right
#align set.sups_empty Set.sups_empty
-/

#print Set.sups_eq_empty /-
@[simp]
theorem sups_eq_empty : s ⊻ t = ∅ ↔ s = ∅ ∨ t = ∅ :=
  image2_eq_empty_iff
#align set.sups_eq_empty Set.sups_eq_empty
-/

/- warning: set.singleton_sups -> Set.singleton_sups is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {t : Set.{u1} α} {a : α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a) t) (Set.image.{u1, u1} α α (fun (b : α) => Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b) t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {t : Set.{u1} α} {a : α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a) t) (Set.image.{u1, u1} α α (fun (b : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b) t)
Case conversion may be inaccurate. Consider using '#align set.singleton_sups Set.singleton_supsₓ'. -/
@[simp]
theorem singleton_sups : {a} ⊻ t = t.image fun b => a ⊔ b :=
  image2_singleton_left
#align set.singleton_sups Set.singleton_sups

/- warning: set.sups_singleton -> Set.sups_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) b)) (Set.image.{u1, u1} α α (fun (a : α) => Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b) s)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) b)) (Set.image.{u1, u1} α α (fun (a : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b) s)
Case conversion may be inaccurate. Consider using '#align set.sups_singleton Set.sups_singletonₓ'. -/
@[simp]
theorem sups_singleton : s ⊻ {b} = s.image fun a => a ⊔ b :=
  image2_singleton_right
#align set.sups_singleton Set.sups_singleton

/- warning: set.singleton_sups_singleton -> Set.singleton_sups_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) b)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) b)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a b))
Case conversion may be inaccurate. Consider using '#align set.singleton_sups_singleton Set.singleton_sups_singletonₓ'. -/
theorem singleton_sups_singleton : ({a} ⊻ {b} : Set α) = {a ⊔ b} :=
  image2_singleton
#align set.singleton_sups_singleton Set.singleton_sups_singleton

/- warning: set.sups_union_left -> Set.sups_union_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) s₁ s₂) t) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₁ t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₂ t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) s₁ s₂) t) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₁ t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₂ t))
Case conversion may be inaccurate. Consider using '#align set.sups_union_left Set.sups_union_leftₓ'. -/
theorem sups_union_left : (s₁ ∪ s₂) ⊻ t = s₁ ⊻ t ∪ s₂ ⊻ t :=
  image2_union_left
#align set.sups_union_left Set.sups_union_left

/- warning: set.sups_union_right -> Set.sups_union_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) t₁ t₂)) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₁) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) t₁ t₂)) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₁) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₂))
Case conversion may be inaccurate. Consider using '#align set.sups_union_right Set.sups_union_rightₓ'. -/
theorem sups_union_right : s ⊻ (t₁ ∪ t₂) = s ⊻ t₁ ∪ s ⊻ t₂ :=
  image2_union_right
#align set.sups_union_right Set.sups_union_right

/- warning: set.sups_inter_subset_left -> Set.sups_inter_subset_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) s₁ s₂) t) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₁ t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₂ t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) s₁ s₂) t) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₁ t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s₂ t))
Case conversion may be inaccurate. Consider using '#align set.sups_inter_subset_left Set.sups_inter_subset_leftₓ'. -/
theorem sups_inter_subset_left : (s₁ ∩ s₂) ⊻ t ⊆ s₁ ⊻ t ∩ s₂ ⊻ t :=
  image2_inter_subset_left
#align set.sups_inter_subset_left Set.sups_inter_subset_left

/- warning: set.sups_inter_subset_right -> Set.sups_inter_subset_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) t₁ t₂)) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₁) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) t₁ t₂)) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₁) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t₂))
Case conversion may be inaccurate. Consider using '#align set.sups_inter_subset_right Set.sups_inter_subset_rightₓ'. -/
theorem sups_inter_subset_right : s ⊻ (t₁ ∩ t₂) ⊆ s ⊻ t₁ ∩ s ⊻ t₂ :=
  image2_inter_subset_right
#align set.sups_inter_subset_right Set.sups_inter_subset_right

variable (s t u v)

/- warning: set.Union_image_sup_left -> Set.unionᵢ_image_sup_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (a : α) => Set.unionᵢ.{u1, 0} α (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) => Set.image.{u1, u1} α α (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a) t))) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (a : α) => Set.unionᵢ.{u1, 0} α (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) (fun (H : Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) => Set.image.{u1, u1} α α ((fun (x._@.Mathlib.Data.Set.Sups._hyg.2587 : α) (x._@.Mathlib.Data.Set.Sups._hyg.2589 : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) x._@.Mathlib.Data.Set.Sups._hyg.2587 x._@.Mathlib.Data.Set.Sups._hyg.2589) a) t))) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)
Case conversion may be inaccurate. Consider using '#align set.Union_image_sup_left Set.unionᵢ_image_sup_leftₓ'. -/
theorem unionᵢ_image_sup_left : (⋃ a ∈ s, (· ⊔ ·) a '' t) = s ⊻ t :=
  unionᵢ_image_left _
#align set.Union_image_sup_left Set.unionᵢ_image_sup_left

/- warning: set.Union_image_sup_right -> Set.unionᵢ_image_sup_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (b : α) => Set.unionᵢ.{u1, 0} α (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) => Set.image.{u1, u1} α α (fun (_x : α) => Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) _x b) s))) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (b : α) => Set.unionᵢ.{u1, 0} α (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) (fun (H : Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) => Set.image.{u1, u1} α α (fun (_x : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) _x b) s))) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)
Case conversion may be inaccurate. Consider using '#align set.Union_image_sup_right Set.unionᵢ_image_sup_rightₓ'. -/
theorem unionᵢ_image_sup_right : (⋃ b ∈ t, (· ⊔ b) '' s) = s ⊻ t :=
  unionᵢ_image_right _
#align set.Union_image_sup_right Set.unionᵢ_image_sup_right

/- warning: set.image_sup_prod -> Set.image_sup_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.image.{u1, u1} (Prod.{u1, u1} α α) α (Function.uncurry.{u1, u1, u1} α α α (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1))) (Set.prod.{u1, u1} α α s t)) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.image2.{u1, u1, u1} α α α (fun (x : α) (x_1 : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) x x_1) s t) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)
Case conversion may be inaccurate. Consider using '#align set.image_sup_prod Set.image_sup_prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem image_sup_prod (s t : Set α) : (s ×ˢ t).image (uncurry (· ⊔ ·)) = s ⊻ t :=
  image_uncurry_prod _ _ _
#align set.image_sup_prod Set.image_sup_prod

#print Set.sups_assoc /-
theorem sups_assoc : s ⊻ t ⊻ u = s ⊻ (t ⊻ u) :=
  image2_assoc fun _ _ _ => sup_assoc
#align set.sups_assoc Set.sups_assoc
-/

#print Set.sups_comm /-
theorem sups_comm : s ⊻ t = t ⊻ s :=
  image2_comm fun _ _ => sup_comm
#align set.sups_comm Set.sups_comm
-/

#print Set.sups_left_comm /-
theorem sups_left_comm : s ⊻ (t ⊻ u) = t ⊻ (s ⊻ u) :=
  image2_left_comm sup_left_comm
#align set.sups_left_comm Set.sups_left_comm
-/

#print Set.sups_right_comm /-
theorem sups_right_comm : s ⊻ t ⊻ u = s ⊻ u ⊻ t :=
  image2_right_comm sup_right_comm
#align set.sups_right_comm Set.sups_right_comm
-/

#print Set.sups_sups_sups_comm /-
theorem sups_sups_sups_comm : s ⊻ t ⊻ (u ⊻ v) = s ⊻ u ⊻ (t ⊻ v) :=
  image2_image2_image2_comm sup_sup_sup_comm
#align set.sups_sups_sups_comm Set.sups_sups_sups_comm
-/

end Sups

section Infs

variable [SemilatticeInf α] (s s₁ s₂ t t₁ t₂ u v : Set α)

#print Set.hasInfs /-
/-- `s ⊼ t` is the set of elements of the form `a ⊓ b` where `a ∈ s`, `b ∈ t`. -/
protected def hasInfs : HasInfs (Set α) :=
  ⟨image2 (· ⊓ ·)⟩
#align set.has_infs Set.hasInfs
-/

scoped[SetFamily] attribute [instance] Set.hasInfs

variable {s s₁ s₂ t t₁ t₂ u} {a b c : α}

/- warning: set.mem_infs -> Set.mem_infs is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {c : α}, Iff (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)) (Exists.{succ u1} α (fun (a : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) => Exists.{succ u1} α (fun (b : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) => Eq.{succ u1} α (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b) c)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {c : α}, Iff (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) c (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)) (Exists.{succ u1} α (fun (a : α) => And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) (Exists.{succ u1} α (fun (b : α) => And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) (Eq.{succ u1} α (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b) c)))))
Case conversion may be inaccurate. Consider using '#align set.mem_infs Set.mem_infsₓ'. -/
@[simp]
theorem mem_infs : c ∈ s ⊼ t ↔ ∃ a ∈ s, ∃ b ∈ t, a ⊓ b = c := by simp [(· ⊼ ·)]
#align set.mem_infs Set.mem_infs

/- warning: set.inf_mem_infs -> Set.inf_mem_infs is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α} {b : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α} {b : α}, (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t))
Case conversion may be inaccurate. Consider using '#align set.inf_mem_infs Set.inf_mem_infsₓ'. -/
theorem inf_mem_infs : a ∈ s → b ∈ t → a ⊓ b ∈ s ⊼ t :=
  mem_image2_of_mem
#align set.inf_mem_infs Set.inf_mem_infs

#print Set.infs_subset /-
theorem infs_subset : s₁ ⊆ s₂ → t₁ ⊆ t₂ → s₁ ⊼ t₁ ⊆ s₂ ⊼ t₂ :=
  image2_subset
#align set.infs_subset Set.infs_subset
-/

#print Set.infs_subset_left /-
theorem infs_subset_left : t₁ ⊆ t₂ → s ⊼ t₁ ⊆ s ⊼ t₂ :=
  image2_subset_left
#align set.infs_subset_left Set.infs_subset_left
-/

#print Set.infs_subset_right /-
theorem infs_subset_right : s₁ ⊆ s₂ → s₁ ⊼ t ⊆ s₂ ⊼ t :=
  image2_subset_right
#align set.infs_subset_right Set.infs_subset_right
-/

/- warning: set.image_subset_infs_left -> Set.image_subset_infs_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {b : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.image.{u1, u1} α α (fun (a : α) => Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b) s) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {b : α}, (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.image.{u1, u1} α α (fun (a : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b) s) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t))
Case conversion may be inaccurate. Consider using '#align set.image_subset_infs_left Set.image_subset_infs_leftₓ'. -/
theorem image_subset_infs_left : b ∈ t → (fun a => a ⊓ b) '' s ⊆ s ⊼ t :=
  image_subset_image2_left
#align set.image_subset_infs_left Set.image_subset_infs_left

/- warning: set.image_subset_infs_right -> Set.image_subset_infs_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.image.{u1, u1} α α (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a) t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {a : α}, (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.image.{u1, u1} α α ((fun (x._@.Mathlib.Data.Set.Sups._hyg.3583 : α) (x._@.Mathlib.Data.Set.Sups._hyg.3585 : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) x._@.Mathlib.Data.Set.Sups._hyg.3583 x._@.Mathlib.Data.Set.Sups._hyg.3585) a) t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t))
Case conversion may be inaccurate. Consider using '#align set.image_subset_infs_right Set.image_subset_infs_rightₓ'. -/
theorem image_subset_infs_right : a ∈ s → (· ⊓ ·) a '' t ⊆ s ⊼ t :=
  image_subset_image2_right
#align set.image_subset_infs_right Set.image_subset_infs_right

/- warning: set.forall_infs_iff -> Set.forall_infs_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {p : α -> Prop}, Iff (forall (c : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)) -> (p c)) (forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (forall (b : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (p (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {p : α -> Prop}, Iff (forall (c : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) c (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)) -> (p c)) (forall (a : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (forall (b : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (p (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b))))
Case conversion may be inaccurate. Consider using '#align set.forall_infs_iff Set.forall_infs_iffₓ'. -/
theorem forall_infs_iff {p : α → Prop} : (∀ c ∈ s ⊼ t, p c) ↔ ∀ a ∈ s, ∀ b ∈ t, p (a ⊓ b) :=
  forall_image2_iff
#align set.forall_infs_iff Set.forall_infs_iff

/- warning: set.infs_subset_iff -> Set.infs_subset_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {u : Set.{u1} α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t) u) (forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (forall (b : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b) u)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t : Set.{u1} α} {u : Set.{u1} α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t) u) (forall (a : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (forall (b : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) -> (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b) u)))
Case conversion may be inaccurate. Consider using '#align set.infs_subset_iff Set.infs_subset_iffₓ'. -/
@[simp]
theorem infs_subset_iff : s ⊼ t ⊆ u ↔ ∀ a ∈ s, ∀ b ∈ t, a ⊓ b ∈ u :=
  image2_subset_iff
#align set.infs_subset_iff Set.infs_subset_iff

#print Set.infs_nonempty /-
@[simp]
theorem infs_nonempty : (s ⊼ t).Nonempty ↔ s.Nonempty ∧ t.Nonempty :=
  image2_nonempty_iff
#align set.infs_nonempty Set.infs_nonempty
-/

#print Set.Nonempty.infs /-
protected theorem Nonempty.infs : s.Nonempty → t.Nonempty → (s ⊼ t).Nonempty :=
  Nonempty.image2
#align set.nonempty.infs Set.Nonempty.infs
-/

#print Set.Nonempty.of_infs_left /-
theorem Nonempty.of_infs_left : (s ⊼ t).Nonempty → s.Nonempty :=
  Nonempty.of_image2_left
#align set.nonempty.of_infs_left Set.Nonempty.of_infs_left
-/

#print Set.Nonempty.of_infs_right /-
theorem Nonempty.of_infs_right : (s ⊼ t).Nonempty → t.Nonempty :=
  Nonempty.of_image2_right
#align set.nonempty.of_infs_right Set.Nonempty.of_infs_right
-/

#print Set.empty_infs /-
@[simp]
theorem empty_infs : ∅ ⊼ t = ∅ :=
  image2_empty_left
#align set.empty_infs Set.empty_infs
-/

#print Set.infs_empty /-
@[simp]
theorem infs_empty : s ⊼ ∅ = ∅ :=
  image2_empty_right
#align set.infs_empty Set.infs_empty
-/

#print Set.infs_eq_empty /-
@[simp]
theorem infs_eq_empty : s ⊼ t = ∅ ↔ s = ∅ ∨ t = ∅ :=
  image2_eq_empty_iff
#align set.infs_eq_empty Set.infs_eq_empty
-/

/- warning: set.singleton_infs -> Set.singleton_infs is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {t : Set.{u1} α} {a : α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a) t) (Set.image.{u1, u1} α α (fun (b : α) => Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b) t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {t : Set.{u1} α} {a : α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a) t) (Set.image.{u1, u1} α α (fun (b : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b) t)
Case conversion may be inaccurate. Consider using '#align set.singleton_infs Set.singleton_infsₓ'. -/
@[simp]
theorem singleton_infs : {a} ⊼ t = t.image fun b => a ⊓ b :=
  image2_singleton_left
#align set.singleton_infs Set.singleton_infs

/- warning: set.infs_singleton -> Set.infs_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) b)) (Set.image.{u1, u1} α α (fun (a : α) => Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b) s)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) b)) (Set.image.{u1, u1} α α (fun (a : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b) s)
Case conversion may be inaccurate. Consider using '#align set.infs_singleton Set.infs_singletonₓ'. -/
@[simp]
theorem infs_singleton : s ⊼ {b} = s.image fun a => a ⊓ b :=
  image2_singleton_right
#align set.infs_singleton Set.infs_singleton

/- warning: set.singleton_infs_singleton -> Set.singleton_infs_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) b)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) a) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) b)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a b))
Case conversion may be inaccurate. Consider using '#align set.singleton_infs_singleton Set.singleton_infs_singletonₓ'. -/
theorem singleton_infs_singleton : ({a} ⊼ {b} : Set α) = {a ⊓ b} :=
  image2_singleton
#align set.singleton_infs_singleton Set.singleton_infs_singleton

/- warning: set.infs_union_left -> Set.infs_union_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) s₁ s₂) t) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₁ t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₂ t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) s₁ s₂) t) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₁ t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₂ t))
Case conversion may be inaccurate. Consider using '#align set.infs_union_left Set.infs_union_leftₓ'. -/
theorem infs_union_left : (s₁ ∪ s₂) ⊼ t = s₁ ⊼ t ∪ s₂ ⊼ t :=
  image2_union_left
#align set.infs_union_left Set.infs_union_left

/- warning: set.infs_union_right -> Set.infs_union_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) t₁ t₂)) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₁) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, Eq.{succ u1} (Set.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) t₁ t₂)) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₁) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₂))
Case conversion may be inaccurate. Consider using '#align set.infs_union_right Set.infs_union_rightₓ'. -/
theorem infs_union_right : s ⊼ (t₁ ∪ t₂) = s ⊼ t₁ ∪ s ⊼ t₂ :=
  image2_union_right
#align set.infs_union_right Set.infs_union_right

/- warning: set.infs_inter_subset_left -> Set.infs_inter_subset_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) s₁ s₂) t) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₁ t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₂ t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s₁ : Set.{u1} α} {s₂ : Set.{u1} α} {t : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) s₁ s₂) t) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₁ t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s₂ t))
Case conversion may be inaccurate. Consider using '#align set.infs_inter_subset_left Set.infs_inter_subset_leftₓ'. -/
theorem infs_inter_subset_left : (s₁ ∩ s₂) ⊼ t ⊆ s₁ ⊼ t ∩ s₂ ⊼ t :=
  image2_inter_subset_left
#align set.infs_inter_subset_left Set.infs_inter_subset_left

/- warning: set.infs_inter_subset_right -> Set.infs_inter_subset_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) t₁ t₂)) (Inter.inter.{u1} (Set.{u1} α) (Set.hasInter.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₁) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] {s : Set.{u1} α} {t₁ : Set.{u1} α} {t₂ : Set.{u1} α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) t₁ t₂)) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₁) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t₂))
Case conversion may be inaccurate. Consider using '#align set.infs_inter_subset_right Set.infs_inter_subset_rightₓ'. -/
theorem infs_inter_subset_right : s ⊼ (t₁ ∩ t₂) ⊆ s ⊼ t₁ ∩ s ⊼ t₂ :=
  image2_inter_subset_right
#align set.infs_inter_subset_right Set.infs_inter_subset_right

variable (s t u v)

/- warning: set.Union_image_inf_left -> Set.unionᵢ_image_inf_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (a : α) => Set.unionᵢ.{u1, 0} α (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) => Set.image.{u1, u1} α α (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a) t))) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (a : α) => Set.unionᵢ.{u1, 0} α (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) (fun (H : Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) => Set.image.{u1, u1} α α ((fun (x._@.Mathlib.Data.Set.Sups._hyg.4535 : α) (x._@.Mathlib.Data.Set.Sups._hyg.4537 : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) x._@.Mathlib.Data.Set.Sups._hyg.4535 x._@.Mathlib.Data.Set.Sups._hyg.4537) a) t))) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)
Case conversion may be inaccurate. Consider using '#align set.Union_image_inf_left Set.unionᵢ_image_inf_leftₓ'. -/
theorem unionᵢ_image_inf_left : (⋃ a ∈ s, (· ⊓ ·) a '' t) = s ⊼ t :=
  unionᵢ_image_left _
#align set.Union_image_inf_left Set.unionᵢ_image_inf_left

/- warning: set.Union_image_inf_right -> Set.unionᵢ_image_inf_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (b : α) => Set.unionᵢ.{u1, 0} α (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b t) => Set.image.{u1, u1} α α (fun (_x : α) => Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) _x b) s))) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.unionᵢ.{u1, succ u1} α α (fun (b : α) => Set.unionᵢ.{u1, 0} α (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) (fun (H : Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) b t) => Set.image.{u1, u1} α α (fun (_x : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) _x b) s))) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)
Case conversion may be inaccurate. Consider using '#align set.Union_image_inf_right Set.unionᵢ_image_inf_rightₓ'. -/
theorem unionᵢ_image_inf_right : (⋃ b ∈ t, (· ⊓ b) '' s) = s ⊼ t :=
  unionᵢ_image_right _
#align set.Union_image_inf_right Set.unionᵢ_image_inf_right

/- warning: set.image_inf_prod -> Set.image_inf_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.image.{u1, u1} (Prod.{u1, u1} α α) α (Function.uncurry.{u1, u1, u1} α α α (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1))) (Set.prod.{u1, u1} α α s t)) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (Set.{u1} α) (Set.image2.{u1, u1, u1} α α α (fun (x : α) (x_1 : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) x x_1) s t) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)
Case conversion may be inaccurate. Consider using '#align set.image_inf_prod Set.image_inf_prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem image_inf_prod (s t : Set α) : (s ×ˢ t).image (uncurry (· ⊓ ·)) = s ⊼ t :=
  image_uncurry_prod _ _ _
#align set.image_inf_prod Set.image_inf_prod

#print Set.infs_assoc /-
theorem infs_assoc : s ⊼ t ⊼ u = s ⊼ (t ⊼ u) :=
  image2_assoc fun _ _ _ => inf_assoc
#align set.infs_assoc Set.infs_assoc
-/

#print Set.infs_comm /-
theorem infs_comm : s ⊼ t = t ⊼ s :=
  image2_comm fun _ _ => inf_comm
#align set.infs_comm Set.infs_comm
-/

#print Set.infs_left_comm /-
theorem infs_left_comm : s ⊼ (t ⊼ u) = t ⊼ (s ⊼ u) :=
  image2_left_comm inf_left_comm
#align set.infs_left_comm Set.infs_left_comm
-/

#print Set.infs_right_comm /-
theorem infs_right_comm : s ⊼ t ⊼ u = s ⊼ u ⊼ t :=
  image2_right_comm inf_right_comm
#align set.infs_right_comm Set.infs_right_comm
-/

#print Set.infs_infs_infs_comm /-
theorem infs_infs_infs_comm : s ⊼ t ⊼ (u ⊼ v) = s ⊼ u ⊼ (t ⊼ v) :=
  image2_image2_image2_comm inf_inf_inf_comm
#align set.infs_infs_infs_comm Set.infs_infs_infs_comm
-/

end Infs

open SetFamily

section DistribLattice

variable [DistribLattice α] (s t u : Set α)

#print Set.sups_infs_subset_left /-
theorem sups_infs_subset_left : s ⊻ t ⊼ u ⊆ (s ⊻ t) ⊼ (s ⊻ u) :=
  image2_distrib_subset_left fun _ _ _ => sup_inf_left
#align set.sups_infs_subset_left Set.sups_infs_subset_left
-/

#print Set.sups_infs_subset_right /-
theorem sups_infs_subset_right : t ⊼ u ⊻ s ⊆ (t ⊻ s) ⊼ (u ⊻ s) :=
  image2_distrib_subset_right fun _ _ _ => sup_inf_right
#align set.sups_infs_subset_right Set.sups_infs_subset_right
-/

#print Set.infs_sups_subset_left /-
theorem infs_sups_subset_left : s ⊼ (t ⊻ u) ⊆ s ⊼ t ⊻ s ⊼ u :=
  image2_distrib_subset_left fun _ _ _ => inf_sup_left
#align set.infs_sups_subset_left Set.infs_sups_subset_left
-/

#print Set.infs_sups_subset_right /-
theorem infs_sups_subset_right : (t ⊻ u) ⊼ s ⊆ t ⊼ s ⊻ u ⊼ s :=
  image2_distrib_subset_right fun _ _ _ => inf_sup_right
#align set.infs_sups_subset_right Set.infs_sups_subset_right
-/

end DistribLattice

end Set

open SetFamily

/- warning: upper_closure_sups -> upperClosure_sups is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (UpperSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))) (upperClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)) (Sup.sup.{u1} (UpperSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))) (UpperSet.hasSup.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))) (upperClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)) s) (upperClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)) t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (UpperSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))) (upperClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)) (HasSups.sups.{u1} (Set.{u1} α) (Set.hasSups.{u1} α _inst_1) s t)) (Sup.sup.{u1} (UpperSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))) (UpperSet.instSupUpperSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))) (upperClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)) s) (upperClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)) t))
Case conversion may be inaccurate. Consider using '#align upper_closure_sups upperClosure_supsₓ'. -/
@[simp]
theorem upperClosure_sups [SemilatticeSup α] (s t : Set α) :
    upperClosure (s ⊻ t) = upperClosure s ⊔ upperClosure t :=
  by
  ext a
  simp only [SetLike.mem_coe, mem_upperClosure, Set.mem_sups, exists_and_left, exists_prop,
    UpperSet.coe_sup, Set.mem_inter_iff]
  constructor
  · rintro ⟨_, ⟨b, hb, c, hc, rfl⟩, ha⟩
    exact ⟨⟨b, hb, le_sup_left.trans ha⟩, c, hc, le_sup_right.trans ha⟩
  · rintro ⟨⟨b, hb, hab⟩, c, hc, hac⟩
    exact ⟨_, ⟨b, hb, c, hc, rfl⟩, sup_le hab hac⟩
#align upper_closure_sups upperClosure_sups

/- warning: lower_closure_infs -> lowerClosure_infs is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (LowerSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))) (lowerClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)) (Inf.inf.{u1} (LowerSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))) (LowerSet.hasInf.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))) (lowerClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)) s) (lowerClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)) t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] (s : Set.{u1} α) (t : Set.{u1} α), Eq.{succ u1} (LowerSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))) (lowerClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)) (HasInfs.infs.{u1} (Set.{u1} α) (Set.hasInfs.{u1} α _inst_1) s t)) (Inf.inf.{u1} (LowerSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))) (LowerSet.instInfLowerSet.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))) (lowerClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)) s) (lowerClosure.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)) t))
Case conversion may be inaccurate. Consider using '#align lower_closure_infs lowerClosure_infsₓ'. -/
@[simp]
theorem lowerClosure_infs [SemilatticeInf α] (s t : Set α) :
    lowerClosure (s ⊼ t) = lowerClosure s ⊓ lowerClosure t :=
  by
  ext a
  simp only [SetLike.mem_coe, mem_lowerClosure, Set.mem_infs, exists_and_left, exists_prop,
    LowerSet.coe_sup, Set.mem_inter_iff]
  constructor
  · rintro ⟨_, ⟨b, hb, c, hc, rfl⟩, ha⟩
    exact ⟨⟨b, hb, ha.trans inf_le_left⟩, c, hc, ha.trans inf_le_right⟩
  · rintro ⟨⟨b, hb, hab⟩, c, hc, hac⟩
    exact ⟨_, ⟨b, hb, c, hc, rfl⟩, le_inf hab hac⟩
#align lower_closure_infs lowerClosure_infs