/-
Copyright (c) 2018 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.semiquot
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Lattice

/-! # Semiquotients

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

A data type for semiquotients, which are classically equivalent to
nonempty sets, but are useful for programming; the idea is that
a semiquotient set `S` represents some (particular but unknown)
element of `S`. This can be used to model nondeterministic functions,
which return something in a range of values (represented by the
predicate `S`) but are not completely determined.
-/

/-- A member of `semiquot α` is classically a nonempty `set α`,
  and in the VM is represented by an element of `α`; the relation
  between these is that the VM element is required to be a member
  of the set `s`. The specific element of `s` that the VM computes
  is hidden by a quotient construction, allowing for the representation
  of nondeterministic functions. -/
structure Semiquot.{u} (α : Type _) where mk' ::
  s : Set α
  val : Trunc ↥s
#align semiquot Semiquotₓ

namespace Semiquot

variable {α : Type _} {β : Type _}

instance : Membership α (Semiquot α) :=
  ⟨fun a q => a ∈ q.s⟩

/- warning: semiquot.mk -> Semiquot.mk is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {a : α} {s : Set.{u_1} α}, (Membership.Mem.{u_1, u_1} α (Set.{u_1} α) (Set.hasMem.{u_1} α) a s) -> (Semiquotₓ.{u_2, u_1} α)
but is expected to have type
  forall {α : Type.{u_1}} {a : α} {s : Set.{u_1} α}, (Membership.mem.{u_1, u_1} α (Set.{u_1} α) (Set.instMembershipSet.{u_1} α) a s) -> (Semiquot.{u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.mk Semiquot.mkₓ'. -/
/-- Construct a `semiquot α` from `h : a ∈ s` where `s : set α`. -/
def mk {a : α} {s : Set α} (h : a ∈ s) : Semiquot α :=
  ⟨s, Trunc.mk ⟨a, h⟩⟩
#align semiquot.mk Semiquot.mk

/- warning: semiquot.ext_s -> Semiquot.ext_s is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {q₁ : Semiquotₓ.{u_2, u_1} α} {q₂ : Semiquotₓ.{u_2, u_1} α}, Iff (Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) q₁ q₂) (Eq.{max (succ u_1) 1} (Set.{u_1} α) (Semiquotₓ.s.{u_2, u_1} α q₁) (Semiquotₓ.s.{u_2, u_1} α q₂))
but is expected to have type
  forall {α : Type.{u_1}} {q₁ : Semiquot.{u_1} α} {q₂ : Semiquot.{u_1} α}, Iff (Eq.{succ u_1} (Semiquot.{u_1} α) q₁ q₂) (Eq.{succ u_1} (Set.{u_1} α) (Semiquot.s.{u_1} α q₁) (Semiquot.s.{u_1} α q₂))
Case conversion may be inaccurate. Consider using '#align semiquot.ext_s Semiquot.ext_sₓ'. -/
theorem ext_s {q₁ q₂ : Semiquot α} : q₁ = q₂ ↔ q₁.s = q₂.s :=
  by
  refine' ⟨congr_arg _, fun h => _⟩
  cases q₁
  cases q₂
  cc
#align semiquot.ext_s Semiquot.ext_s

/- warning: semiquot.ext -> Semiquot.ext is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {q₁ : Semiquotₓ.{u_2, u_1} α} {q₂ : Semiquotₓ.{u_2, u_1} α}, Iff (Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) q₁ q₂) (forall (a : α), Iff (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a q₁) (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a q₂))
but is expected to have type
  forall {α : Type.{u_1}} {q₁ : Semiquot.{u_1} α} {q₂ : Semiquot.{u_1} α}, Iff (Eq.{succ u_1} (Semiquot.{u_1} α) q₁ q₂) (forall (a : α), Iff (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q₁) (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q₂))
Case conversion may be inaccurate. Consider using '#align semiquot.ext Semiquot.extₓ'. -/
theorem ext {q₁ q₂ : Semiquot α} : q₁ = q₂ ↔ ∀ a, a ∈ q₁ ↔ a ∈ q₂ :=
  ext_s.trans Set.ext_iff
#align semiquot.ext Semiquot.ext

/- warning: semiquot.exists_mem -> Semiquot.exists_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (q : Semiquotₓ.{u_2, u_1} α), Exists.{succ u_1} α (fun (a : α) => Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a q)
but is expected to have type
  forall {α : Type.{u_1}} (q : Semiquot.{u_1} α), Exists.{succ u_1} α (fun (a : α) => Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q)
Case conversion may be inaccurate. Consider using '#align semiquot.exists_mem Semiquot.exists_memₓ'. -/
theorem exists_mem (q : Semiquot α) : ∃ a, a ∈ q :=
  let ⟨⟨a, h⟩, h₂⟩ := q.2.exists_rep
  ⟨a, h⟩
#align semiquot.exists_mem Semiquot.exists_mem

/- warning: semiquot.eq_mk_of_mem -> Semiquot.eq_mk_of_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {q : Semiquotₓ.{u_2, u_1} α} {a : α} (h : Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a q), Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) q (Semiquot.mk.{u_1, u_2} α a (Semiquotₓ.s.{u_2, u_1} α q) h)
but is expected to have type
  forall {α : Type.{u_1}} {q : Semiquot.{u_1} α} {a : α} (h : Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q), Eq.{succ u_1} (Semiquot.{u_1} α) q (Semiquot.mk.{u_1} α a (Semiquot.s.{u_1} α q) h)
Case conversion may be inaccurate. Consider using '#align semiquot.eq_mk_of_mem Semiquot.eq_mk_of_memₓ'. -/
theorem eq_mk_of_mem {q : Semiquot α} {a : α} (h : a ∈ q) : q = @mk _ a q.1 h :=
  ext_s.2 rfl
#align semiquot.eq_mk_of_mem Semiquot.eq_mk_of_mem

/- warning: semiquot.nonempty -> Semiquot.nonempty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (q : Semiquotₓ.{u_2, u_1} α), Set.Nonempty.{u_1} α (Semiquotₓ.s.{u_2, u_1} α q)
but is expected to have type
  forall {α : Type.{u_1}} (q : Semiquot.{u_1} α), Set.Nonempty.{u_1} α (Semiquot.s.{u_1} α q)
Case conversion may be inaccurate. Consider using '#align semiquot.nonempty Semiquot.nonemptyₓ'. -/
theorem nonempty (q : Semiquot α) : q.s.Nonempty :=
  q.exists_mem
#align semiquot.nonempty Semiquot.nonempty

/- warning: semiquot.pure -> Semiquot.pure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}}, α -> (Semiquotₓ.{u_2, u_1} α)
but is expected to have type
  forall {α : Type.{u_1}}, α -> (Semiquot.{u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.pure Semiquot.pureₓ'. -/
/-- `pure a` is `a` reinterpreted as an unspecified element of `{a}`. -/
protected def pure (a : α) : Semiquot α :=
  mk (Set.mem_singleton a)
#align semiquot.pure Semiquot.pure

/- warning: semiquot.mem_pure' -> Semiquot.mem_pure' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {a : α} {b : α}, Iff (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a (Semiquot.pure.{u_1, u_2} α b)) (Eq.{succ u_1} α a b)
but is expected to have type
  forall {α : Type.{u_1}} {a : α} {b : α}, Iff (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a (Semiquot.pure.{u_1} α b)) (Eq.{succ u_1} α a b)
Case conversion may be inaccurate. Consider using '#align semiquot.mem_pure' Semiquot.mem_pure'ₓ'. -/
@[simp]
theorem mem_pure' {a b : α} : a ∈ Semiquot.pure b ↔ a = b :=
  Set.mem_singleton_iff
#align semiquot.mem_pure' Semiquot.mem_pure'

/- warning: semiquot.blur' -> Semiquot.blur' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (q : Semiquotₓ.{u_2, u_1} α) {s : Set.{u_1} α}, (HasSubset.Subset.{u_1} (Set.{u_1} α) (Set.hasSubset.{u_1} α) (Semiquotₓ.s.{u_2, u_1} α q) s) -> (Semiquotₓ.{u_3, u_1} α)
but is expected to have type
  forall {α : Type.{u_1}} (q : Semiquot.{u_1} α) {s : Set.{u_1} α}, (HasSubset.Subset.{u_1} (Set.{u_1} α) (Set.instHasSubsetSet.{u_1} α) (Semiquot.s.{u_1} α q) s) -> (Semiquot.{u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.blur' Semiquot.blur'ₓ'. -/
/-- Replace `s` in a `semiquot` with a superset. -/
def blur' (q : Semiquot α) {s : Set α} (h : q.s ⊆ s) : Semiquot α :=
  ⟨s, Trunc.lift (fun a : q.s => Trunc.mk ⟨a.1, h a.2⟩) (fun _ _ => Trunc.eq _ _) q.2⟩
#align semiquot.blur' Semiquot.blur'

/- warning: semiquot.blur -> Semiquot.blur is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}}, (Set.{u_1} α) -> (Semiquotₓ.{u_2, u_1} α) -> (Semiquotₓ.{u_3, u_1} α)
but is expected to have type
  forall {α : Type.{u_1}}, (Set.{u_1} α) -> (Semiquot.{u_1} α) -> (Semiquot.{u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.blur Semiquot.blurₓ'. -/
/-- Replace `s` in a `q : semiquot α` with a union `s ∪ q.s` -/
def blur (s : Set α) (q : Semiquot α) : Semiquot α :=
  blur' q (Set.subset_union_right s q.s)
#align semiquot.blur Semiquot.blur

/- warning: semiquot.blur_eq_blur' -> Semiquot.blur_eq_blur' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (q : Semiquotₓ.{u_2, u_1} α) (s : Set.{u_1} α) (h : HasSubset.Subset.{u_1} (Set.{u_1} α) (Set.hasSubset.{u_1} α) (Semiquotₓ.s.{u_2, u_1} α q) s), Eq.{succ u_1} (Semiquotₓ.{u_3, u_1} α) (Semiquot.blur.{u_1, u_2, u_3} α s q) (Semiquot.blur'.{u_1, u_2, u_3} α q s h)
but is expected to have type
  forall {α : Type.{u_1}} (q : Semiquot.{u_1} α) (s : Set.{u_1} α) (h : HasSubset.Subset.{u_1} (Set.{u_1} α) (Set.instHasSubsetSet.{u_1} α) (Semiquot.s.{u_1} α q) s), Eq.{succ u_1} (Semiquot.{u_1} α) (Semiquot.blur.{u_1} α s q) (Semiquot.blur'.{u_1} α q s h)
Case conversion may be inaccurate. Consider using '#align semiquot.blur_eq_blur' Semiquot.blur_eq_blur'ₓ'. -/
theorem blur_eq_blur' (q : Semiquot α) (s : Set α) (h : q.s ⊆ s) : blur s q = blur' q h := by
  unfold blur <;> congr <;> exact Set.union_eq_self_of_subset_right h
#align semiquot.blur_eq_blur' Semiquot.blur_eq_blur'

/- warning: semiquot.mem_blur' -> Semiquot.mem_blur' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (q : Semiquotₓ.{u_2, u_1} α) {s : Set.{u_1} α} (h : HasSubset.Subset.{u_1} (Set.{u_1} α) (Set.hasSubset.{u_1} α) (Semiquotₓ.s.{u_2, u_1} α q) s) {a : α}, Iff (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a (Semiquot.blur'.{u_1, u_2, u_3} α q s h)) (Membership.Mem.{u_1, u_1} α (Set.{u_1} α) (Set.hasMem.{u_1} α) a s)
but is expected to have type
  forall {α : Type.{u_1}} (q : Semiquot.{u_1} α) {s : Set.{u_1} α} (h : HasSubset.Subset.{u_1} (Set.{u_1} α) (Set.instHasSubsetSet.{u_1} α) (Semiquot.s.{u_1} α q) s) {a : α}, Iff (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a (Semiquot.blur'.{u_1} α q s h)) (Membership.mem.{u_1, u_1} α (Set.{u_1} α) (Set.instMembershipSet.{u_1} α) a s)
Case conversion may be inaccurate. Consider using '#align semiquot.mem_blur' Semiquot.mem_blur'ₓ'. -/
@[simp]
theorem mem_blur' (q : Semiquot α) {s : Set α} (h : q.s ⊆ s) {a : α} : a ∈ blur' q h ↔ a ∈ s :=
  Iff.rfl
#align semiquot.mem_blur' Semiquot.mem_blur'

/- warning: semiquot.of_trunc -> Semiquot.ofTrunc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}}, (Trunc.{succ u_1} α) -> (Semiquotₓ.{u_2, u_1} α)
but is expected to have type
  forall {α : Type.{u_1}}, (Trunc.{succ u_1} α) -> (Semiquot.{u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.of_trunc Semiquot.ofTruncₓ'. -/
/-- Convert a `trunc α` to a `semiquot α`. -/
def ofTrunc (q : Trunc α) : Semiquot α :=
  ⟨Set.univ, q.map fun a => ⟨a, trivial⟩⟩
#align semiquot.of_trunc Semiquot.ofTrunc

/- warning: semiquot.to_trunc -> Semiquot.toTrunc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}}, (Semiquotₓ.{u_2, u_1} α) -> (Trunc.{succ u_1} α)
but is expected to have type
  forall {α : Type.{u_1}}, (Semiquot.{u_1} α) -> (Trunc.{succ u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.to_trunc Semiquot.toTruncₓ'. -/
/-- Convert a `semiquot α` to a `trunc α`. -/
def toTrunc (q : Semiquot α) : Trunc α :=
  q.2.map Subtype.val
#align semiquot.to_trunc Semiquot.toTrunc

/- warning: semiquot.lift_on -> Semiquot.liftOn is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_2}} (q : Semiquotₓ.{u_3, u_1} α) (f : α -> β), (forall (a : α), (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a q) -> (forall (b : α), (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) b q) -> (Eq.{succ u_2} β (f a) (f b)))) -> β
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_2}} (q : Semiquot.{u_1} α) (f : α -> β), (forall (a : α), (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q) -> (forall (b : α), (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) b q) -> (Eq.{succ u_2} β (f a) (f b)))) -> β
Case conversion may be inaccurate. Consider using '#align semiquot.lift_on Semiquot.liftOnₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (a b «expr ∈ » q) -/
/-- If `f` is a constant on `q.s`, then `q.lift_on f` is the value of `f`
at any point of `q`. -/
def liftOn (q : Semiquot α) (f : α → β) (h : ∀ (a) (_ : a ∈ q) (b) (_ : b ∈ q), f a = f b) : β :=
  Trunc.liftOn q.2 (fun x => f x.1) fun x y => h _ x.2 _ y.2
#align semiquot.lift_on Semiquot.liftOn

/- warning: semiquot.lift_on_of_mem -> Semiquot.liftOn_ofMem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_2}} (q : Semiquotₓ.{u_3, u_1} α) (f : α -> β) (h : forall (a : α), (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a q) -> (forall (b : α), (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) b q) -> (Eq.{succ u_2} β (f a) (f b)))) (a : α), (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a q) -> (Eq.{succ u_2} β (Semiquot.liftOn.{u_1, u_2, u_3} α β q f h) (f a))
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_2}} (q : Semiquot.{u_1} α) (f : α -> β) (h : forall (a : α), (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q) -> (forall (b : α), (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) b q) -> (Eq.{succ u_2} β (f a) (f b)))) (a : α), (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q) -> (Eq.{succ u_2} β (Semiquot.liftOn.{u_1, u_2} α β q f h) (f a))
Case conversion may be inaccurate. Consider using '#align semiquot.lift_on_of_mem Semiquot.liftOn_ofMemₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (a b «expr ∈ » q) -/
theorem liftOn_ofMem (q : Semiquot α) (f : α → β) (h : ∀ (a) (_ : a ∈ q) (b) (_ : b ∈ q), f a = f b)
    (a : α) (aq : a ∈ q) : liftOn q f h = f a := by
  revert h <;> rw [eq_mk_of_mem aq] <;> intro <;> rfl
#align semiquot.lift_on_of_mem Semiquot.liftOn_ofMem

/- warning: semiquot.map -> Semiquot.map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_2}}, (α -> β) -> (Semiquotₓ.{u_3, u_1} α) -> (Semiquotₓ.{u_4, u_2} β)
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_2}}, (α -> β) -> (Semiquot.{u_1} α) -> (Semiquot.{u_2} β)
Case conversion may be inaccurate. Consider using '#align semiquot.map Semiquot.mapₓ'. -/
/-- Apply a function to the unknown value stored in a `semiquot α`. -/
def map (f : α → β) (q : Semiquot α) : Semiquot β :=
  ⟨f '' q.1, q.2.map fun x => ⟨f x.1, Set.mem_image_of_mem _ x.2⟩⟩
#align semiquot.map Semiquot.map

/- warning: semiquot.mem_map -> Semiquot.mem_map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_2}} (f : α -> β) (q : Semiquotₓ.{u_3, u_1} α) (b : β), Iff (Membership.Mem.{u_2, u_2} β (Semiquotₓ.{u_4, u_2} β) (Semiquotₓ.hasMem.{u_2, u_4} β) b (Semiquot.map.{u_1, u_2, u_3, u_4} α β f q)) (Exists.{succ u_1} α (fun (a : α) => And (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a q) (Eq.{succ u_2} β (f a) b)))
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_2}} (f : α -> β) (q : Semiquot.{u_1} α) (b : β), Iff (Membership.mem.{u_2, u_2} β (Semiquot.{u_2} β) (Semiquot.instMembershipSemiquot.{u_2} β) b (Semiquot.map.{u_1, u_2} α β f q)) (Exists.{succ u_1} α (fun (a : α) => And (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q) (Eq.{succ u_2} β (f a) b)))
Case conversion may be inaccurate. Consider using '#align semiquot.mem_map Semiquot.mem_mapₓ'. -/
@[simp]
theorem mem_map (f : α → β) (q : Semiquot α) (b : β) : b ∈ map f q ↔ ∃ a, a ∈ q ∧ f a = b :=
  Set.mem_image _ _ _
#align semiquot.mem_map Semiquot.mem_map

/- warning: semiquot.bind -> Semiquot.bind is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_2}}, (Semiquotₓ.{u_3, u_1} α) -> (α -> (Semiquotₓ.{u_4, u_2} β)) -> (Semiquotₓ.{u_5, u_2} β)
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_2}}, (Semiquot.{u_1} α) -> (α -> (Semiquot.{u_2} β)) -> (Semiquot.{u_2} β)
Case conversion may be inaccurate. Consider using '#align semiquot.bind Semiquot.bindₓ'. -/
/-- Apply a function returning a `semiquot` to a `semiquot`. -/
def bind (q : Semiquot α) (f : α → Semiquot β) : Semiquot β :=
  ⟨⋃ a ∈ q.1, (f a).1, q.2.bind fun a => (f a.1).2.map fun b => ⟨b.1, Set.mem_bunionᵢ a.2 b.2⟩⟩
#align semiquot.bind Semiquot.bind

/- warning: semiquot.mem_bind -> Semiquot.mem_bind is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_2}} (q : Semiquotₓ.{u_3, u_1} α) (f : α -> (Semiquotₓ.{u_4, u_2} β)) (b : β), Iff (Membership.Mem.{u_2, u_2} β (Semiquotₓ.{u_5, u_2} β) (Semiquotₓ.hasMem.{u_2, u_5} β) b (Semiquot.bind.{u_1, u_2, u_3, u_4, u_5} α β q f)) (Exists.{succ u_1} α (fun (a : α) => Exists.{0} (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a q) (fun (H : Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_3, u_1} α) (Semiquotₓ.hasMem.{u_1, u_3} α) a q) => Membership.Mem.{u_2, u_2} β (Semiquotₓ.{u_4, u_2} β) (Semiquotₓ.hasMem.{u_2, u_4} β) b (f a))))
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_2}} (q : Semiquot.{u_1} α) (f : α -> (Semiquot.{u_2} β)) (b : β), Iff (Membership.mem.{u_2, u_2} β (Semiquot.{u_2} β) (Semiquot.instMembershipSemiquot.{u_2} β) b (Semiquot.bind.{u_1, u_2} α β q f)) (Exists.{succ u_1} α (fun (a : α) => And (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a q) (Membership.mem.{u_2, u_2} β (Semiquot.{u_2} β) (Semiquot.instMembershipSemiquot.{u_2} β) b (f a))))
Case conversion may be inaccurate. Consider using '#align semiquot.mem_bind Semiquot.mem_bindₓ'. -/
@[simp]
theorem mem_bind (q : Semiquot α) (f : α → Semiquot β) (b : β) : b ∈ bind q f ↔ ∃ a ∈ q, b ∈ f a :=
  Set.mem_unionᵢ₂
#align semiquot.mem_bind Semiquot.mem_bind

instance : Monad Semiquot where
  pure := @Semiquot.pure
  map := @Semiquot.map
  bind := @Semiquot.bind

/- warning: semiquot.map_def -> Semiquot.map_def is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_1}}, Eq.{succ u_1} ((α -> β) -> (Semiquotₓ.{u_2, u_1} α) -> (Semiquotₓ.{u_2, u_1} β)) (Functor.map.{u_1, u_1} (fun {α : Type.{u_1}} => Semiquotₓ.{u_2, u_1} α) (Applicative.toFunctor.{u_1, u_1} (fun {α : Type.{u_1}} => Semiquotₓ.{u_2, u_1} α) (Monad.toApplicative.{u_1, u_1} (fun {α : Type.{u_1}} => Semiquotₓ.{u_2, u_1} α) Semiquotₓ.monad.{u_1, u_2})) α β) (Semiquot.map.{u_1, u_1, u_2, u_2} α β)
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_1}}, Eq.{succ u_1} ((α -> β) -> (Semiquot.{u_1} α) -> (Semiquot.{u_1} β)) (fun (x._@.Mathlib.Data.Semiquot._hyg.993 : α -> β) (x._@.Mathlib.Data.Semiquot._hyg.995 : Semiquot.{u_1} α) => Functor.map.{u_1, u_1} Semiquot.{u_1} (Applicative.toFunctor.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α β x._@.Mathlib.Data.Semiquot._hyg.993 x._@.Mathlib.Data.Semiquot._hyg.995) (Semiquot.map.{u_1, u_1} α β)
Case conversion may be inaccurate. Consider using '#align semiquot.map_def Semiquot.map_defₓ'. -/
@[simp]
theorem map_def {β} : ((· <$> ·) : (α → β) → Semiquot α → Semiquot β) = map :=
  rfl
#align semiquot.map_def Semiquot.map_def

/- warning: semiquot.bind_def -> Semiquot.bind_def is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {β : Type.{u_1}}, Eq.{succ u_1} ((Semiquotₓ.{u_2, u_1} α) -> (α -> (Semiquotₓ.{u_2, u_1} β)) -> (Semiquotₓ.{u_2, u_1} β)) (Bind.bind.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toHasBind.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2}) α β) (Semiquot.bind.{u_1, u_1, u_2, u_2, u_2} α β)
but is expected to have type
  forall {α : Type.{u_1}} {β : Type.{u_1}}, Eq.{succ u_1} ((Semiquot.{u_1} α) -> (α -> (Semiquot.{u_1} β)) -> (Semiquot.{u_1} β)) (fun (x._@.Mathlib.Data.Semiquot._hyg.1033 : Semiquot.{u_1} α) (x._@.Mathlib.Data.Semiquot._hyg.1035 : α -> (Semiquot.{u_1} β)) => Bind.bind.{u_1, u_1} Semiquot.{u_1} (Monad.toBind.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1}) α β x._@.Mathlib.Data.Semiquot._hyg.1033 x._@.Mathlib.Data.Semiquot._hyg.1035) (Semiquot.bind.{u_1, u_1} α β)
Case conversion may be inaccurate. Consider using '#align semiquot.bind_def Semiquot.bind_defₓ'. -/
@[simp]
theorem bind_def {β} : ((· >>= ·) : Semiquot α → (α → Semiquot β) → Semiquot β) = bind :=
  rfl
#align semiquot.bind_def Semiquot.bind_def

/- warning: semiquot.mem_pure -> Semiquot.mem_pure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {a : α} {b : α}, Iff (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α b)) (Eq.{succ u_1} α a b)
but is expected to have type
  forall {α : Type.{u_1}} {a : α} {b : α}, Iff (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α b)) (Eq.{succ u_1} α a b)
Case conversion may be inaccurate. Consider using '#align semiquot.mem_pure Semiquot.mem_pureₓ'. -/
@[simp]
theorem mem_pure {a b : α} : a ∈ (pure b : Semiquot α) ↔ a = b :=
  Set.mem_singleton_iff
#align semiquot.mem_pure Semiquot.mem_pure

/- warning: semiquot.mem_pure_self -> Semiquot.mem_pure_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (a : α), Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α a)
but is expected to have type
  forall {α : Type.{u_1}} (a : α), Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α a)
Case conversion may be inaccurate. Consider using '#align semiquot.mem_pure_self Semiquot.mem_pure_selfₓ'. -/
theorem mem_pure_self (a : α) : a ∈ (pure a : Semiquot α) :=
  Set.mem_singleton a
#align semiquot.mem_pure_self Semiquot.mem_pure_self

/- warning: semiquot.pure_inj -> Semiquot.pure_inj is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {a : α} {b : α}, Iff (Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α a) (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α b)) (Eq.{succ u_1} α a b)
but is expected to have type
  forall {α : Type.{u_1}} {a : α} {b : α}, Iff (Eq.{succ u_1} (Semiquot.{u_1} α) (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α a) (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α b)) (Eq.{succ u_1} α a b)
Case conversion may be inaccurate. Consider using '#align semiquot.pure_inj Semiquot.pure_injₓ'. -/
@[simp]
theorem pure_inj {a b : α} : (pure a : Semiquot α) = pure b ↔ a = b :=
  ext_s.trans Set.singleton_eq_singleton_iff
#align semiquot.pure_inj Semiquot.pure_inj

instance : LawfulMonad Semiquot
    where
  pure_bind α β x f := ext.2 <| by simp
  bind_assoc α β γ s f g :=
    ext.2 <| by
      simp <;>
        exact fun c =>
          ⟨fun ⟨b, ⟨a, as, bf⟩, cg⟩ => ⟨a, as, b, bf, cg⟩, fun ⟨a, as, b, bf, cg⟩ =>
            ⟨b, ⟨a, as, bf⟩, cg⟩⟩
  id_map α q := ext.2 <| by simp
  bind_pure_comp_eq_map α β f s := ext.2 <| by simp [eq_comm]

instance : LE (Semiquot α) :=
  ⟨fun s t => s.s ⊆ t.s⟩

instance : PartialOrder (Semiquot α)
    where
  le s t := ∀ ⦃x⦄, x ∈ s → x ∈ t
  le_refl s := Set.Subset.refl _
  le_trans s t u := Set.Subset.trans
  le_antisymm s t h₁ h₂ := ext_s.2 (Set.Subset.antisymm h₁ h₂)

instance : SemilatticeSup (Semiquot α) :=
  { Semiquot.partialOrder with
    sup := fun s => blur s.s
    le_sup_left := fun s t => Set.subset_union_left _ _
    le_sup_right := fun s t => Set.subset_union_right _ _
    sup_le := fun s t u => Set.union_subset }

/- warning: semiquot.pure_le -> Semiquot.pure_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {a : α} {s : Semiquotₓ.{u_2, u_1} α}, Iff (LE.le.{u_1} (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasLe.{u_1, u_2} α) (Pure.pure.{u_1, u_1} (fun {α : Type.{u_1}} => Semiquotₓ.{u_2, u_1} α) (Applicative.toHasPure.{u_1, u_1} (fun {α : Type.{u_1}} => Semiquotₓ.{u_2, u_1} α) (Monad.toApplicative.{u_1, u_1} (fun {α : Type.{u_1}} => Semiquotₓ.{u_2, u_1} α) Semiquotₓ.monad.{u_1, u_2})) α a) s) (Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a s)
but is expected to have type
  forall {α : Type.{u_1}} {a : α} {s : Semiquot.{u_1} α}, Iff (LE.le.{u_1} (Semiquot.{u_1} α) (Semiquot.instLESemiquot.{u_1} α) (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α a) s) (Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a s)
Case conversion may be inaccurate. Consider using '#align semiquot.pure_le Semiquot.pure_leₓ'. -/
@[simp]
theorem pure_le {a : α} {s : Semiquot α} : pure a ≤ s ↔ a ∈ s :=
  Set.singleton_subset_iff
#align semiquot.pure_le Semiquot.pure_le

/- warning: semiquot.is_pure -> Semiquot.IsPure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}}, (Semiquotₓ.{u_2, u_1} α) -> Prop
but is expected to have type
  forall {α : Type.{u_1}}, (Semiquot.{u_1} α) -> Prop
Case conversion may be inaccurate. Consider using '#align semiquot.is_pure Semiquot.IsPureₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (a b «expr ∈ » q) -/
/-- Assert that a `semiquot` contains only one possible value. -/
def IsPure (q : Semiquot α) : Prop :=
  ∀ (a) (_ : a ∈ q) (b) (_ : b ∈ q), a = b
#align semiquot.is_pure Semiquot.IsPure

/- warning: semiquot.get -> Semiquot.get is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (q : Semiquotₓ.{u_2, u_1} α), (Semiquot.IsPure.{u_1, u_2} α q) -> α
but is expected to have type
  forall {α : Type.{u_1}} (q : Semiquot.{u_1} α), (Semiquot.IsPure.{u_1} α q) -> α
Case conversion may be inaccurate. Consider using '#align semiquot.get Semiquot.getₓ'. -/
/-- Extract the value from a `is_pure` semiquotient. -/
def get (q : Semiquot α) (h : q.IsPure) : α :=
  liftOn q id h
#align semiquot.get Semiquot.get

/- warning: semiquot.get_mem -> Semiquot.get_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {q : Semiquotₓ.{u_2, u_1} α} (p : Semiquot.IsPure.{u_1, u_2} α q), Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) (Semiquot.get.{u_1, u_2} α q p) q
but is expected to have type
  forall {α : Type.{u_1}} {q : Semiquot.{u_1} α} (p : Semiquot.IsPure.{u_1} α q), Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) (Semiquot.get.{u_1} α q p) q
Case conversion may be inaccurate. Consider using '#align semiquot.get_mem Semiquot.get_memₓ'. -/
theorem get_mem {q : Semiquot α} (p) : get q p ∈ q :=
  by
  let ⟨a, h⟩ := exists_mem q
  unfold get <;> rw [lift_on_of_mem q _ _ a h] <;> exact h
#align semiquot.get_mem Semiquot.get_mem

/- warning: semiquot.eq_pure -> Semiquot.eq_pure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {q : Semiquotₓ.{u_2, u_1} α} (p : Semiquot.IsPure.{u_1, u_2} α q), Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) q (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α (Semiquot.get.{u_1, u_2} α q p))
but is expected to have type
  forall {α : Type.{u_1}} {q : Semiquot.{u_1} α} (p : Semiquot.IsPure.{u_1} α q), Eq.{succ u_1} (Semiquot.{u_1} α) q (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α (Semiquot.get.{u_1} α q p))
Case conversion may be inaccurate. Consider using '#align semiquot.eq_pure Semiquot.eq_pureₓ'. -/
theorem eq_pure {q : Semiquot α} (p) : q = pure (get q p) :=
  ext.2 fun a => by simp <;> exact ⟨fun h => p _ h _ (get_mem _), fun e => e.symm ▸ get_mem _⟩
#align semiquot.eq_pure Semiquot.eq_pure

/- warning: semiquot.pure_is_pure -> Semiquot.pure_isPure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (a : α), Semiquot.IsPure.{u_1, u_2} α (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α a)
but is expected to have type
  forall {α : Type.{u_1}} (a : α), Semiquot.IsPure.{u_1} α (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α a)
Case conversion may be inaccurate. Consider using '#align semiquot.pure_is_pure Semiquot.pure_isPureₓ'. -/
@[simp]
theorem pure_isPure (a : α) : IsPure (pure a)
  | b, ab, c, ac => by
    rw [mem_pure] at ab ac
    cc
#align semiquot.pure_is_pure Semiquot.pure_isPure

/- warning: semiquot.is_pure_iff -> Semiquot.isPure_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {s : Semiquotₓ.{u_2, u_1} α}, Iff (Semiquot.IsPure.{u_1, u_2} α s) (Exists.{succ u_1} α (fun (a : α) => Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) s (Pure.pure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Applicative.toHasPure.{u_1, u_1} Semiquotₓ.{u_2, u_1} (Monad.toApplicative.{u_1, u_1} Semiquotₓ.{u_2, u_1} Semiquotₓ.monad.{u_1, u_2})) α a)))
but is expected to have type
  forall {α : Type.{u_1}} {s : Semiquot.{u_1} α}, Iff (Semiquot.IsPure.{u_1} α s) (Exists.{succ u_1} α (fun (a : α) => Eq.{succ u_1} (Semiquot.{u_1} α) s (Pure.pure.{u_1, u_1} Semiquot.{u_1} (Applicative.toPure.{u_1, u_1} Semiquot.{u_1} (Monad.toApplicative.{u_1, u_1} Semiquot.{u_1} Semiquot.instMonadSemiquot.{u_1})) α a)))
Case conversion may be inaccurate. Consider using '#align semiquot.is_pure_iff Semiquot.isPure_iffₓ'. -/
theorem isPure_iff {s : Semiquot α} : IsPure s ↔ ∃ a, s = pure a :=
  ⟨fun h => ⟨_, eq_pure h⟩, fun ⟨a, e⟩ => e.symm ▸ pure_isPure _⟩
#align semiquot.is_pure_iff Semiquot.isPure_iff

/- warning: semiquot.is_pure.mono -> Semiquot.IsPure.mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {s : Semiquotₓ.{u_2, u_1} α} {t : Semiquotₓ.{u_2, u_1} α}, (LE.le.{u_1} (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasLe.{u_1, u_2} α) s t) -> (Semiquot.IsPure.{u_1, u_2} α t) -> (Semiquot.IsPure.{u_1, u_2} α s)
but is expected to have type
  forall {α : Type.{u_1}} {s : Semiquot.{u_1} α} {t : Semiquot.{u_1} α}, (LE.le.{u_1} (Semiquot.{u_1} α) (Semiquot.instLESemiquot.{u_1} α) s t) -> (Semiquot.IsPure.{u_1} α t) -> (Semiquot.IsPure.{u_1} α s)
Case conversion may be inaccurate. Consider using '#align semiquot.is_pure.mono Semiquot.IsPure.monoₓ'. -/
theorem IsPure.mono {s t : Semiquot α} (st : s ≤ t) (h : IsPure t) : IsPure s
  | a, as, b, bs => h _ (st as) _ (st bs)
#align semiquot.is_pure.mono Semiquot.IsPure.mono

/- warning: semiquot.is_pure.min -> Semiquot.IsPure.min is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} {s : Semiquotₓ.{u_2, u_1} α} {t : Semiquotₓ.{u_2, u_1} α}, (Semiquot.IsPure.{u_1, u_2} α t) -> (Iff (LE.le.{u_1} (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasLe.{u_1, u_2} α) s t) (Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) s t))
but is expected to have type
  forall {α : Type.{u_1}} {s : Semiquot.{u_1} α} {t : Semiquot.{u_1} α}, (Semiquot.IsPure.{u_1} α t) -> (Iff (LE.le.{u_1} (Semiquot.{u_1} α) (Semiquot.instLESemiquot.{u_1} α) s t) (Eq.{succ u_1} (Semiquot.{u_1} α) s t))
Case conversion may be inaccurate. Consider using '#align semiquot.is_pure.min Semiquot.IsPure.minₓ'. -/
theorem IsPure.min {s t : Semiquot α} (h : IsPure t) : s ≤ t ↔ s = t :=
  ⟨fun st =>
    le_antisymm st <| by
      rw [eq_pure h, eq_pure (h.mono st)] <;> simp <;> exact h _ (get_mem _) _ (st <| get_mem _),
    le_of_eq⟩
#align semiquot.is_pure.min Semiquot.IsPure.min

/- warning: semiquot.is_pure_of_subsingleton -> Semiquot.isPure_of_subsingleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} [_inst_1 : Subsingleton.{succ u_1} α] (q : Semiquotₓ.{u_2, u_1} α), Semiquot.IsPure.{u_1, u_2} α q
but is expected to have type
  forall {α : Type.{u_1}} [_inst_1 : Subsingleton.{succ u_1} α] (q : Semiquot.{u_1} α), Semiquot.IsPure.{u_1} α q
Case conversion may be inaccurate. Consider using '#align semiquot.is_pure_of_subsingleton Semiquot.isPure_of_subsingletonₓ'. -/
theorem isPure_of_subsingleton [Subsingleton α] (q : Semiquot α) : IsPure q
  | a, b, aq, bq => Subsingleton.elim _ _
#align semiquot.is_pure_of_subsingleton Semiquot.isPure_of_subsingleton

/- warning: semiquot.univ -> Semiquot.univ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} [_inst_1 : Inhabited.{succ u_1} α], Semiquotₓ.{u_2, u_1} α
but is expected to have type
  forall {α : Type.{u_1}} [_inst_1 : Inhabited.{succ u_1} α], Semiquot.{u_1} α
Case conversion may be inaccurate. Consider using '#align semiquot.univ Semiquot.univₓ'. -/
/-- `univ : semiquot α` represents an unspecified element of `univ : set α`. -/
def univ [Inhabited α] : Semiquot α :=
  mk <| Set.mem_univ default
#align semiquot.univ Semiquot.univ

instance [Inhabited α] : Inhabited (Semiquot α) :=
  ⟨univ⟩

/- warning: semiquot.mem_univ -> Semiquot.mem_univ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} [_inst_1 : Inhabited.{succ u_1} α] (a : α), Membership.Mem.{u_1, u_1} α (Semiquotₓ.{u_2, u_1} α) (Semiquotₓ.hasMem.{u_1, u_2} α) a (Semiquot.univ.{u_1, u_2} α _inst_1)
but is expected to have type
  forall {α : Type.{u_1}} [_inst_1 : Inhabited.{succ u_1} α] (a : α), Membership.mem.{u_1, u_1} α (Semiquot.{u_1} α) (Semiquot.instMembershipSemiquot.{u_1} α) a (Semiquot.univ.{u_1} α _inst_1)
Case conversion may be inaccurate. Consider using '#align semiquot.mem_univ Semiquot.mem_univₓ'. -/
@[simp]
theorem mem_univ [Inhabited α] : ∀ a, a ∈ @univ α _ :=
  @Set.mem_univ α
#align semiquot.mem_univ Semiquot.mem_univ

/- warning: semiquot.univ_unique -> Semiquot.univ_unique is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} (I : Inhabited.{succ u_1} α) (J : Inhabited.{succ u_1} α), Eq.{succ u_1} (Semiquotₓ.{u_2, u_1} α) (Semiquot.univ.{u_1, u_2} α I) (Semiquot.univ.{u_1, u_2} α J)
but is expected to have type
  forall {α : Type.{u_1}} (I : Inhabited.{succ u_1} α) (J : Inhabited.{succ u_1} α), Eq.{succ u_1} (Semiquot.{u_1} α) (Semiquot.univ.{u_1} α I) (Semiquot.univ.{u_1} α J)
Case conversion may be inaccurate. Consider using '#align semiquot.univ_unique Semiquot.univ_uniqueₓ'. -/
@[congr]
theorem univ_unique (I J : Inhabited α) : @univ _ I = @univ _ J :=
  ext.2 <| by simp
#align semiquot.univ_unique Semiquot.univ_unique

/- warning: semiquot.is_pure_univ -> Semiquot.isPure_univ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u_1}} [_inst_1 : Inhabited.{succ u_1} α], Iff (Semiquot.IsPure.{u_1, u_2} α (Semiquot.univ.{u_1, u_2} α _inst_1)) (Subsingleton.{succ u_1} α)
but is expected to have type
  forall {α : Type.{u_1}} [_inst_1 : Inhabited.{succ u_1} α], Iff (Semiquot.IsPure.{u_1} α (Semiquot.univ.{u_1} α _inst_1)) (Subsingleton.{succ u_1} α)
Case conversion may be inaccurate. Consider using '#align semiquot.is_pure_univ Semiquot.isPure_univₓ'. -/
@[simp]
theorem isPure_univ [Inhabited α] : @IsPure α univ ↔ Subsingleton α :=
  ⟨fun h => ⟨fun a b => h a trivial b trivial⟩, fun ⟨h⟩ a _ b _ => h a b⟩
#align semiquot.is_pure_univ Semiquot.isPure_univ

instance [Inhabited α] : OrderTop (Semiquot α)
    where
  top := univ
  le_top s := Set.subset_univ _

end Semiquot


import list.simple_value
       list.modify.basic

open list

/-- Man in the middle for all-chain or all-loop situations -/
theorem MITM_baby (tf : ℤ) (L1 L2 : list ℤ) (d : ℤ) (h : list.modify L1 L2 d)
  (n : ℕ) (hL1 : L1.length = n) (hL2 : L2.length = n) (i : fin n) :
  abs (list.value_i tf n L1 i hL1 - list.value_i tf n L2 i hL2) ≤ d :=
begin
  revert L1 L2, -- so the inductive hypothesis does not depend on the exact choice of L1 and L2
  induction n with e he, -- by induction on the number of components

    -- base case : n = 0
    {cases i.is_lt},

    -- inductive step : n = nat.succ e
    {intros L1 L2 h hL1 hL2,
    unfold list.value_i,

    /- after unfolding, the goal is :
       abs
       (aux_fun (nth_le L1 (i.val) _) tf (min' (of_fn (λ (j : fin e), value_i tf e (remove_nth L1 (i.val)) j _))) -
        aux_fun (nth_le L2 (i.val) _) tf (min' (of_fn (λ (j : fin e), value_i tf e (remove_nth L2 (i.val)) j _))))
        ≤ d
    -/

    /- now either we opened the component in which the games differ or not-/
    by_cases hin : h.n = i.val,

      { -- i = place where lists differ
        have heq := h.heq,
        rw hin at heq, -- after substitution, heq is remove_nth L1 (i.val) = remove_nth L2 (i.val)
        simp only [heq], -- simp only rewrites in the arguments of nth_le as well
        apply list.aux_fun_L1, -- see list.simple_value
        convert h.bound, -- goal is basically just h.bound
          exact hin.symm, /- need to prove i.val = h.n, as the goal described the indices
                             using i.val, and h.bound used h.n-/
          exact hin.symm, -- because i.val/h.n comes up twice
      },

      { -- i ≠ place where lists differ
           /- so this time by another lemma nth_le L1 (i.val) _ = nth_le L2 (i.val) _ -/
        rw list.modify_same h i.val _ (begin rw hL2, exact i.is_lt end) hin, -- see list.modify.basic
        apply list.aux_fun_L2, -- see list.simple_value
        -- apply "lists differ by at most d -> min differs by at most d"
        apply list.min'_change, -- see list.min.basic

          {simp only [list.length_of_fn]}, -- use that length (of_fn (λ (j : fin e), ...))) = e

          --prove 0 <= d from h using that the absolute value is non-negative
          {apply le_trans _ h.bound, -- transitivity of ≤
          show abs (nth_le L1 h.n h.ha - nth_le L2 h.n h.hb) ≥ 0,
          exact abs_nonneg _,}, -- absolute values are non-negative

          {intros n HnL HnM,
          rw eq_comm at hin, -- commutativity of =

          --need the following as argument for he (in he L1 = (remove_nth L1 (i.val))
          --and L2 = (remove_nth L2 (i.val)))
          have P : list.modify (remove_nth L1 (i.val)) (remove_nth L2 (i.val)) d,
            {exact list.modify_remove_nth h i.val hin}, -- see list.modify.basic

          -- put the goal into the correct form for the inductive hypothis he
          rw length_of_fn at HnL,
          rw nth_le_of_fn _ ⟨n, HnL⟩, /- nth_le (of_fn f) ⟨n, HnL⟩ = f (⟨n, HnL⟩)
                                        (where f is : λ (j : fin e), value_i tf e (remove_nth L1 (i.val)) j _)) -/
          rw nth_le_of_fn _ ⟨n, HnL⟩,

          exact he _ _ _ P _ _, /- use inductive hypothesis
                                   (every argument other than P can be inferred)-/
          },
      },
   }
end


/-
Copyright (c) 2022 Alexander Bentkamp. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alexander Bentkamp, Jeremy Avigad, Johan Commelin

! This file was ported from Lean 3 source module linear_algebra.matrix.schur_complement
! leanprover-community/mathlib commit 70fd9563a21e7b963887c9360bd29b2393e6225a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.Matrix.NonsingularInverse
import Mathbin.LinearAlgebra.Matrix.PosDef

/-! # Schur complement

This file proves properties of the Schur complement `D - C A⁻¹ B` of a block matrix `[A B; C D]`.

The determinant of a block matrix in terms of the Schur complement is expressed in the lemmas
`matrix.det_from_blocks₁₁` and `matrix.det_from_blocks₂₂` in the file
`linear_algebra.matrix.nonsingular_inverse`.

## Main result

 * `matrix.schur_complement_pos_semidef_iff` : If a matrix `A` is positive definite, then `[A B; Bᴴ
  D]` is postive semidefinite if and only if `D - Bᴴ A⁻¹ B` is postive semidefinite.

-/

namespace Matrix

open Matrix

variable {n : Type _} {m : Type _} {𝕜 : Type _} [IsROrC 𝕜]

-- mathport name: «expr ⊕ᵥ »
scoped infixl:65 " ⊕ᵥ " => Sum.elim

theorem schur_complement_eq₁₁ [Fintype m] [DecidableEq m] [Fintype n] {A : Matrix m m 𝕜}
    (B : Matrix m n 𝕜) (D : Matrix n n 𝕜) (x : m → 𝕜) (y : n → 𝕜) [Invertible A]
    (hA : A.IsHermitian) :
    vecMul (star (x ⊕ᵥ y)) (fromBlocks A B Bᴴ D) ⬝ᵥ (x ⊕ᵥ y) =
      vecMul (star (x + (A⁻¹ ⬝ B).mulVec y)) A ⬝ᵥ (x + (A⁻¹ ⬝ B).mulVec y) +
        vecMul (star y) (D - Bᴴ ⬝ A⁻¹ ⬝ B) ⬝ᵥ y :=
  by
  simp [Function.star_sum_elim, from_blocks_mul_vec, vec_mul_from_blocks, add_vec_mul,
    dot_product_mul_vec, vec_mul_sub, Matrix.mul_assoc, vec_mul_mul_vec, hA.eq,
    conj_transpose_nonsing_inv, star_mul_vec]
  abel
#align matrix.schur_complement_eq₁₁ Matrix.schur_complement_eq₁₁

theorem schur_complement_eq₂₂ [Fintype m] [Fintype n] [DecidableEq n] (A : Matrix m m 𝕜)
    (B : Matrix m n 𝕜) {D : Matrix n n 𝕜} (x : m → 𝕜) (y : n → 𝕜) [Invertible D]
    (hD : D.IsHermitian) :
    vecMul (star (x ⊕ᵥ y)) (fromBlocks A B Bᴴ D) ⬝ᵥ (x ⊕ᵥ y) =
      vecMul (star ((D⁻¹ ⬝ Bᴴ).mulVec x + y)) D ⬝ᵥ ((D⁻¹ ⬝ Bᴴ).mulVec x + y) +
        vecMul (star x) (A - B ⬝ D⁻¹ ⬝ Bᴴ) ⬝ᵥ x :=
  by
  simp [Function.star_sum_elim, from_blocks_mul_vec, vec_mul_from_blocks, add_vec_mul,
    dot_product_mul_vec, vec_mul_sub, Matrix.mul_assoc, vec_mul_mul_vec, hD.eq,
    conj_transpose_nonsing_inv, star_mul_vec]
  abel
#align matrix.schur_complement_eq₂₂ Matrix.schur_complement_eq₂₂

end Matrix

namespace Matrix

open Matrix

variable {n : Type _} {m : Type _} {𝕜 : Type _} [IsROrC 𝕜]

theorem IsHermitian.from_blocks₁₁ [Fintype m] [DecidableEq m] {A : Matrix m m 𝕜} (B : Matrix m n 𝕜)
    (D : Matrix n n 𝕜) (hA : A.IsHermitian) :
    (fromBlocks A B Bᴴ D).IsHermitian ↔ (D - Bᴴ ⬝ A⁻¹ ⬝ B).IsHermitian :=
  by
  have hBAB : (Bᴴ ⬝ A⁻¹ ⬝ B).IsHermitian :=
    by
    apply is_hermitian_conj_transpose_mul_mul
    apply hA.inv
  rw [is_hermitian_from_blocks_iff]
  constructor
  · intro h
    apply is_hermitian.sub h.2.2.2 hBAB
  · intro h
    refine' ⟨hA, rfl, conj_transpose_conj_transpose B, _⟩
    rw [← sub_add_cancel D]
    apply is_hermitian.add h hBAB
#align matrix.is_hermitian.from_blocks₁₁ Matrix.IsHermitian.from_blocks₁₁

theorem IsHermitian.from_blocks₂₂ [Fintype n] [DecidableEq n] (A : Matrix m m 𝕜) (B : Matrix m n 𝕜)
    {D : Matrix n n 𝕜} (hD : D.IsHermitian) :
    (fromBlocks A B Bᴴ D).IsHermitian ↔ (A - B ⬝ D⁻¹ ⬝ Bᴴ).IsHermitian :=
  by
  rw [← is_hermitian_submatrix_equiv (Equiv.sumComm n m), Equiv.sumComm_apply,
    from_blocks_submatrix_sum_swap_sum_swap]
  convert is_hermitian.from_blocks₁₁ _ _ hD <;> simp
#align matrix.is_hermitian.from_blocks₂₂ Matrix.IsHermitian.from_blocks₂₂

theorem PosSemidef.from_blocks₁₁ [Fintype m] [DecidableEq m] [Fintype n] {A : Matrix m m 𝕜}
    (B : Matrix m n 𝕜) (D : Matrix n n 𝕜) (hA : A.PosDef) [Invertible A] :
    (fromBlocks A B Bᴴ D).PosSemidef ↔ (D - Bᴴ ⬝ A⁻¹ ⬝ B).PosSemidef :=
  by
  rw [pos_semidef, is_hermitian.from_blocks₁₁ _ _ hA.1]
  constructor
  · refine' fun h => ⟨h.1, fun x => _⟩
    have := h.2 (-(A⁻¹ ⬝ B).mulVec x ⊕ᵥ x)
    rw [dot_product_mul_vec, schur_complement_eq₁₁ B D _ _ hA.1, neg_add_self, dot_product_zero,
      zero_add] at this
    rw [dot_product_mul_vec]
    exact this
  · refine' fun h => ⟨h.1, fun x => _⟩
    rw [dot_product_mul_vec, ← Sum.elim_comp_inl_inr x, schur_complement_eq₁₁ B D _ _ hA.1, map_add]
    apply le_add_of_nonneg_of_le
    · rw [← dot_product_mul_vec]
      apply hA.pos_semidef.2
    · rw [← dot_product_mul_vec]
      apply h.2
#align matrix.pos_semidef.from_blocks₁₁ Matrix.PosSemidef.from_blocks₁₁

theorem PosSemidef.from_blocks₂₂ [Fintype m] [Fintype n] [DecidableEq n] (A : Matrix m m 𝕜)
    (B : Matrix m n 𝕜) {D : Matrix n n 𝕜} (hD : D.PosDef) [Invertible D] :
    (fromBlocks A B Bᴴ D).PosSemidef ↔ (A - B ⬝ D⁻¹ ⬝ Bᴴ).PosSemidef :=
  by
  rw [← pos_semidef_submatrix_equiv (Equiv.sumComm n m), Equiv.sumComm_apply,
    from_blocks_submatrix_sum_swap_sum_swap]
  convert pos_semidef.from_blocks₁₁ _ _ hD <;> first |infer_instance|simp
#align matrix.pos_semidef.from_blocks₂₂ Matrix.PosSemidef.from_blocks₂₂

end Matrix


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

! This file was ported from Lean 3 source module data.set.pairwise.basic
! leanprover-community/mathlib commit c227d107bbada5d0d9d20287e3282c0a7f1651a0
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Set.Function
import Mathlib.Logic.Relation
import Mathlib.Logic.Pairwise

/-!
# Relations holding pairwise

This file develops pairwise relations and defines pairwise disjoint indexed sets.

We also prove many basic facts about `Pairwise`. It is possible that an intermediate file,
with more imports than `Logic.Pairwise` but not importing `Data.Set.Function` would be appropriate
to hold many of these basic facts.

## Main declarations

* `Set.PairwiseDisjoint`: `s.PairwiseDisjoint f` states that images under `f` of distinct elements
  of `s` are either equal or `Disjoint`.

## Notes

The spelling `s.PairwiseDisjoint id` is preferred over `s.Pairwise Disjoint` to permit dot notation
on `Set.PairwiseDisjoint`, even though the latter unfolds to something nicer.
-/

open Set Function

variable {α β γ ι ι' : Type _} {r p q : α → α → Prop}

section Pairwise

variable {f g : ι → α} {s t u : Set α} {a b : α}

theorem pairwise_on_bool (hr : Symmetric r) {a b : α} :
    Pairwise (r on fun c => cond c a b) ↔ r a b := by simpa [Pairwise, Function.onFun] using @hr a b
#align pairwise_on_bool pairwise_on_bool

theorem pairwise_disjoint_on_bool [SemilatticeInf α] [OrderBot α] {a b : α} :
    Pairwise (Disjoint on fun c => cond c a b) ↔ Disjoint a b :=
  pairwise_on_bool Disjoint.symm
#align pairwise_disjoint_on_bool pairwise_disjoint_on_bool

theorem Symmetric.pairwise_on [LinearOrder ι] (hr : Symmetric r) (f : ι → α) :
    Pairwise (r on f) ↔ ∀ ⦃m n⦄, m < n → r (f m) (f n) :=
  ⟨fun h _m _n hmn => h hmn.ne, fun h _m _n hmn => hmn.lt_or_lt.elim (@h _ _) fun h' => hr (h h')⟩
#align symmetric.pairwise_on Symmetric.pairwise_on

theorem pairwise_disjoint_on [SemilatticeInf α] [OrderBot α] [LinearOrder ι] (f : ι → α) :
    Pairwise (Disjoint on f) ↔ ∀ ⦃m n⦄, m < n → Disjoint (f m) (f n) :=
  Symmetric.pairwise_on Disjoint.symm f
#align pairwise_disjoint_on pairwise_disjoint_on

theorem pairwise_disjoint_mono [SemilatticeInf α] [OrderBot α] (hs : Pairwise (Disjoint on f))
    (h : g ≤ f) : Pairwise (Disjoint on g) :=
  hs.mono fun i j hij => Disjoint.mono (h i) (h j) hij
#align pairwise_disjoint.mono pairwise_disjoint_mono

namespace Set

theorem Pairwise.mono (h : t ⊆ s) (hs : s.Pairwise r) : t.Pairwise r :=
  fun _x xt _y yt => hs (h xt) (h yt)
#align set.pairwise.mono Set.Pairwise.mono

theorem Pairwise.mono' (H : r ≤ p) (hr : s.Pairwise r) : s.Pairwise p :=
  hr.imp H
#align set.pairwise.mono' Set.Pairwise.mono'

theorem pairwise_top (s : Set α) : s.Pairwise ⊤ :=
  pairwise_of_forall s _ fun _ _ => trivial
#align set.pairwise_top Set.pairwise_top

protected theorem Subsingleton.pairwise (h : s.Subsingleton) (r : α → α → Prop) : s.Pairwise r :=
  fun _x hx _y hy hne => (hne (h hx hy)).elim
#align set.subsingleton.pairwise Set.Subsingleton.pairwise

@[simp]
theorem pairwise_empty (r : α → α → Prop) : (∅ : Set α).Pairwise r :=
  subsingleton_empty.pairwise r
#align set.pairwise_empty Set.pairwise_empty

@[simp]
theorem pairwise_singleton (a : α) (r : α → α → Prop) : Set.Pairwise {a} r :=
  subsingleton_singleton.pairwise r
#align set.pairwise_singleton Set.pairwise_singleton

theorem pairwise_iff_of_refl [IsRefl α r] : s.Pairwise r ↔ ∀ ⦃a⦄, a ∈ s → ∀ ⦃b⦄, b ∈ s → r a b :=
  forall₄_congr fun _ _ _ _ => or_iff_not_imp_left.symm.trans <| or_iff_right_of_imp of_eq
#align set.pairwise_iff_of_refl Set.pairwise_iff_of_refl

alias pairwise_iff_of_refl ↔ Pairwise.of_refl _
#align set.pairwise.of_refl Set.Pairwise.of_refl

theorem Nonempty.pairwise_iff_exists_forall [IsEquiv α r] {s : Set ι} (hs : s.Nonempty) :
    s.Pairwise (r on f) ↔ ∃ z, ∀ x ∈ s, r (f x) z := by
  constructor
  · rcases hs with ⟨y, hy⟩
    refine' fun H => ⟨f y, fun x hx => _⟩
    rcases eq_or_ne x y with (rfl | hne)
    · apply IsRefl.refl
    · exact H hx hy hne
  · rintro ⟨z, hz⟩ x hx y hy _
    exact @IsTrans.trans α r _ (f x) z (f y) (hz _ hx) (IsSymm.symm _ _ <| hz _ hy)
#align set.nonempty.pairwise_iff_exists_forall Set.Nonempty.pairwise_iff_exists_forall

/-- For a nonempty set `s`, a function `f` takes pairwise equal values on `s` if and only if
for some `z` in the codomain, `f` takes value `z` on all `x ∈ s`. See also
`Set.pairwise_eq_iff_exists_eq` for a version that assumes `[Nonempty ι]` instead of
`Set.Nonempty s`. -/
theorem Nonempty.pairwise_eq_iff_exists_eq {s : Set α} (hs : s.Nonempty) {f : α → ι} :
    (s.Pairwise fun x y => f x = f y) ↔ ∃ z, ∀ x ∈ s, f x = z :=
  hs.pairwise_iff_exists_forall
#align set.nonempty.pairwise_eq_iff_exists_eq Set.Nonempty.pairwise_eq_iff_exists_eq

theorem pairwise_iff_exists_forall [Nonempty ι] (s : Set α) (f : α → ι) {r : ι → ι → Prop}
    [IsEquiv ι r] : s.Pairwise (r on f) ↔ ∃ z, ∀ x ∈ s, r (f x) z := by
  rcases s.eq_empty_or_nonempty with (rfl | hne)
  · simp
  · exact hne.pairwise_iff_exists_forall
#align set.pairwise_iff_exists_forall Set.pairwise_iff_exists_forall

/-- A function `f : α → ι` with nonempty codomain takes pairwise equal values on a set `s` if and
only if for some `z` in the codomain, `f` takes value `z` on all `x ∈ s`. See also
`Set.Nonempty.pairwise_eq_iff_exists_eq` for a version that assumes `Set.Nonempty s` instead of
`[Nonempty ι]`. -/
theorem pairwise_eq_iff_exists_eq [Nonempty ι] (s : Set α) (f : α → ι) :
    (s.Pairwise fun x y => f x = f y) ↔ ∃ z, ∀ x ∈ s, f x = z :=
  pairwise_iff_exists_forall s f
#align set.pairwise_eq_iff_exists_eq Set.pairwise_eq_iff_exists_eq

theorem pairwise_union :
  (s ∪ t).Pairwise r ↔
    s.Pairwise r ∧ t.Pairwise r ∧ ∀ a ∈ s, ∀ b ∈ t, a ≠ b → r a b ∧ r b a := by
  simp only [Set.Pairwise, mem_union, or_imp, forall_and]
  exact
    ⟨fun H => ⟨H.1.1, H.2.2, H.2.1, fun x hx y hy hne => H.1.2 y hy x hx hne.symm⟩, fun H =>
      ⟨⟨H.1, fun x hx y hy hne => H.2.2.2 y hy x hx hne.symm⟩, H.2.2.1, H.2.1⟩⟩
#align set.pairwise_union Set.pairwise_union

theorem pairwise_union_of_symmetric (hr : Symmetric r) :
    (s ∪ t).Pairwise r ↔ s.Pairwise r ∧ t.Pairwise r ∧ ∀ a ∈ s, ∀ b ∈ t, a ≠ b → r a b :=
  pairwise_union.trans <| by simp only [hr.iff, and_self_iff]
#align set.pairwise_union_of_symmetric Set.pairwise_union_of_symmetric

theorem pairwise_insert :
    (insert a s).Pairwise r ↔ s.Pairwise r ∧ ∀ b ∈ s, a ≠ b → r a b ∧ r b a := by
  simp only [insert_eq, pairwise_union, pairwise_singleton, true_and_iff, mem_singleton_iff,
    forall_eq]
#align set.pairwise_insert Set.pairwise_insert

theorem pairwise_insert_of_not_mem (ha : a ∉ s) :
    (insert a s).Pairwise r ↔ s.Pairwise r ∧ ∀ b ∈ s, r a b ∧ r b a :=
  pairwise_insert.trans <|
    and_congr_right' <| forall₂_congr fun b hb => by simp [(ne_of_mem_of_not_mem hb ha).symm]
#align set.pairwise_insert_of_not_mem Set.pairwise_insert_of_not_mem

protected theorem Pairwise.insert (hs : s.Pairwise r) (h : ∀ b ∈ s, a ≠ b → r a b ∧ r b a) :
    (insert a s).Pairwise r :=
  pairwise_insert.2 ⟨hs, h⟩
#align set.pairwise.insert Set.Pairwise.insert

theorem Pairwise.insert_of_not_mem (ha : a ∉ s) (hs : s.Pairwise r) (h : ∀ b ∈ s, r a b ∧ r b a) :
    (insert a s).Pairwise r :=
  (pairwise_insert_of_not_mem ha).2 ⟨hs, h⟩
#align set.pairwise.insert_of_not_mem Set.Pairwise.insert_of_not_mem

theorem pairwise_insert_of_symmetric (hr : Symmetric r) :
    (insert a s).Pairwise r ↔ s.Pairwise r ∧ ∀ b ∈ s, a ≠ b → r a b := by
  simp only [pairwise_insert, hr.iff a, and_self_iff]
#align set.pairwise_insert_of_symmetric Set.pairwise_insert_of_symmetric

theorem pairwise_insert_of_symmetric_of_not_mem (hr : Symmetric r) (ha : a ∉ s) :
    (insert a s).Pairwise r ↔ s.Pairwise r ∧ ∀ b ∈ s, r a b := by
  simp only [pairwise_insert_of_not_mem ha, hr.iff a, and_self_iff]
#align set.pairwise_insert_of_symmetric_of_not_mem Set.pairwise_insert_of_symmetric_of_not_mem

theorem Pairwise.insert_of_symmetric (hs : s.Pairwise r) (hr : Symmetric r)
    (h : ∀ b ∈ s, a ≠ b → r a b) : (insert a s).Pairwise r :=
  (pairwise_insert_of_symmetric hr).2 ⟨hs, h⟩
#align set.pairwise.insert_of_symmetric Set.Pairwise.insert_of_symmetric

theorem Pairwise.insert_of_symmetric_of_not_mem (hs : s.Pairwise r) (hr : Symmetric r) (ha : a ∉ s)
    (h : ∀ b ∈ s, r a b) : (insert a s).Pairwise r :=
  (pairwise_insert_of_symmetric_of_not_mem hr ha).2 ⟨hs, h⟩
#align set.pairwise.insert_of_symmetric_of_not_mem Set.Pairwise.insert_of_symmetric_of_not_mem

theorem pairwise_pair : Set.Pairwise {a, b} r ↔ a ≠ b → r a b ∧ r b a := by simp [pairwise_insert]
#align set.pairwise_pair Set.pairwise_pair

theorem pairwise_pair_of_symmetric (hr : Symmetric r) : Set.Pairwise {a, b} r ↔ a ≠ b → r a b := by
  simp [pairwise_insert_of_symmetric hr]
#align set.pairwise_pair_of_symmetric Set.pairwise_pair_of_symmetric

theorem pairwise_univ : (univ : Set α).Pairwise r ↔ Pairwise r := by
  simp only [Set.Pairwise, Pairwise, mem_univ, forall_const]
#align set.pairwise_univ Set.pairwise_univ

@[simp]
theorem pairwise_bot_iff : s.Pairwise (⊥ : α → α → Prop) ↔ (s : Set α).Subsingleton :=
  ⟨fun h _a ha _b hb => h.eq ha hb id, fun h => h.pairwise _⟩
#align set.pairwise_bot_iff Set.pairwise_bot_iff

alias pairwise_bot_iff ↔ Pairwise.subsingleton _
#align set.pairwise.subsingleton Set.Pairwise.subsingleton

theorem InjOn.pairwise_image {s : Set ι} (h : s.InjOn f) :
    (f '' s).Pairwise r ↔ s.Pairwise (r on f) := by
  simp (config := { contextual := true }) [h.eq_iff, Set.Pairwise]
#align set.inj_on.pairwise_image Set.InjOn.pairwise_image

end Set

end Pairwise

theorem pairwise_subtype_iff_pairwise_set (s : Set α) (r : α → α → Prop) :
    (Pairwise fun (x : s) (y : s) => r x y) ↔ s.Pairwise r := by
  simp only [Pairwise, Set.Pairwise, SetCoe.forall, Ne.def, Subtype.ext_iff, Subtype.coe_mk]
#align pairwise_subtype_iff_pairwise_set pairwise_subtype_iff_pairwise_set

alias pairwise_subtype_iff_pairwise_set ↔ Pairwise.set_of_subtype Set.Pairwise.subtype
#align pairwise.set_of_subtype Pairwise.set_of_subtype
#align set.pairwise.subtype Set.Pairwise.subtype

namespace Set

section PartialOrderBot

variable [PartialOrder α] [OrderBot α] {s t : Set ι} {f g : ι → α}

/-- A set is `PairwiseDisjoint` under `f`, if the images of any distinct two elements under `f`
are disjoint.

`s.Pairwise Disjoint` is (definitionally) the same as `s.PairwiseDisjoint id`. We prefer the latter
in order to allow dot notation on `Set.PairwiseDisjoint`, even though the former unfolds more
nicely. -/
def PairwiseDisjoint (s : Set ι) (f : ι → α) : Prop :=
  s.Pairwise (Disjoint on f)
#align set.pairwise_disjoint Set.PairwiseDisjoint

theorem PairwiseDisjoint.subset (ht : t.PairwiseDisjoint f) (h : s ⊆ t) : s.PairwiseDisjoint f :=
  Pairwise.mono h ht
#align set.pairwise_disjoint.subset Set.PairwiseDisjoint.subset

theorem PairwiseDisjoint.mono_on (hs : s.PairwiseDisjoint f) (h : ∀ ⦃i⦄, i ∈ s → g i ≤ f i) :
    s.PairwiseDisjoint g := fun _a ha _b hb hab => (hs ha hb hab).mono (h ha) (h hb)
#align set.pairwise_disjoint.mono_on Set.PairwiseDisjoint.mono_on

theorem PairwiseDisjoint.mono (hs : s.PairwiseDisjoint f) (h : g ≤ f) : s.PairwiseDisjoint g :=
  hs.mono_on fun i _ => h i
#align set.pairwise_disjoint.mono Set.PairwiseDisjoint.mono

@[simp]
theorem pairwiseDisjoint_empty : (∅ : Set ι).PairwiseDisjoint f :=
  pairwise_empty _
#align set.pairwise_disjoint_empty Set.pairwiseDisjoint_empty

@[simp]
theorem pairwiseDisjoint_singleton (i : ι) (f : ι → α) : PairwiseDisjoint {i} f :=
  pairwise_singleton i _
#align set.pairwise_disjoint_singleton Set.pairwiseDisjoint_singleton

theorem pairwiseDisjoint_insert {i : ι} :
    (insert i s).PairwiseDisjoint f ↔
      s.PairwiseDisjoint f ∧ ∀ j ∈ s, i ≠ j → Disjoint (f i) (f j) :=
  pairwise_insert_of_symmetric <| symmetric_disjoint.comap f
#align set.pairwise_disjoint_insert Set.pairwiseDisjoint_insert

theorem pairwiseDisjoint_insert_of_not_mem {i : ι} (hi : i ∉ s) :
    (insert i s).PairwiseDisjoint f ↔ s.PairwiseDisjoint f ∧ ∀ j ∈ s, Disjoint (f i) (f j) :=
  pairwise_insert_of_symmetric_of_not_mem (symmetric_disjoint.comap f) hi
#align set.pairwise_disjoint_insert_of_not_mem Set.pairwiseDisjoint_insert_of_not_mem

protected theorem PairwiseDisjoint.insert (hs : s.PairwiseDisjoint f) {i : ι}
    (h : ∀ j ∈ s, i ≠ j → Disjoint (f i) (f j)) : (insert i s).PairwiseDisjoint f :=
  pairwiseDisjoint_insert.2 ⟨hs, h⟩
#align set.pairwise_disjoint.insert Set.PairwiseDisjoint.insert

theorem PairwiseDisjoint.insert_of_not_mem (hs : s.PairwiseDisjoint f) {i : ι} (hi : i ∉ s)
    (h : ∀ j ∈ s, Disjoint (f i) (f j)) : (insert i s).PairwiseDisjoint f :=
  (pairwiseDisjoint_insert_of_not_mem hi).2 ⟨hs, h⟩
#align set.pairwise_disjoint.insert_of_not_mem Set.PairwiseDisjoint.insert_of_not_mem

theorem PairwiseDisjoint.image_of_le (hs : s.PairwiseDisjoint f) {g : ι → ι} (hg : f ∘ g ≤ f) :
    (g '' s).PairwiseDisjoint f := by
  rintro _ ⟨a, ha, rfl⟩ _ ⟨b, hb, rfl⟩ h
  exact (hs ha hb <| ne_of_apply_ne _ h).mono (hg a) (hg b)
#align set.pairwise_disjoint.image_of_le Set.PairwiseDisjoint.image_of_le

theorem InjOn.pairwiseDisjoint_image {g : ι' → ι} {s : Set ι'} (h : s.InjOn g) :
    (g '' s).PairwiseDisjoint f ↔ s.PairwiseDisjoint (f ∘ g) :=
  h.pairwise_image
#align set.inj_on.pairwise_disjoint_image Set.InjOn.pairwiseDisjoint_image

theorem PairwiseDisjoint.range (g : s → ι) (hg : ∀ i : s, f (g i) ≤ f i)
    (ht : s.PairwiseDisjoint f) : (range g).PairwiseDisjoint f := by
  rintro _ ⟨x, rfl⟩ _ ⟨y, rfl⟩ hxy
  exact ((ht x.2 y.2) fun h => hxy <| congr_arg g <| Subtype.ext h).mono (hg x) (hg y)
#align set.pairwise_disjoint.range Set.PairwiseDisjoint.range

theorem pairwiseDisjoint_union :
    (s ∪ t).PairwiseDisjoint f ↔
      s.PairwiseDisjoint f ∧
        t.PairwiseDisjoint f ∧ ∀ ⦃i⦄, i ∈ s → ∀ ⦃j⦄, j ∈ t → i ≠ j → Disjoint (f i) (f j) :=
  pairwise_union_of_symmetric <| symmetric_disjoint.comap f
#align set.pairwise_disjoint_union Set.pairwiseDisjoint_union

theorem PairwiseDisjoint.union (hs : s.PairwiseDisjoint f) (ht : t.PairwiseDisjoint f)
    (h : ∀ ⦃i⦄, i ∈ s → ∀ ⦃j⦄, j ∈ t → i ≠ j → Disjoint (f i) (f j)) : (s ∪ t).PairwiseDisjoint f :=
  pairwiseDisjoint_union.2 ⟨hs, ht, h⟩
#align set.pairwise_disjoint.union Set.PairwiseDisjoint.union

-- classical
theorem PairwiseDisjoint.elim (hs : s.PairwiseDisjoint f) {i j : ι} (hi : i ∈ s) (hj : j ∈ s)
    (h : ¬Disjoint (f i) (f j)) : i = j :=
  hs.eq hi hj h
#align set.pairwise_disjoint.elim Set.PairwiseDisjoint.elim

end PartialOrderBot

section SemilatticeInfBot

variable [SemilatticeInf α] [OrderBot α] {s t : Set ι} {f g : ι → α}

-- classical
theorem PairwiseDisjoint.elim' (hs : s.PairwiseDisjoint f) {i j : ι} (hi : i ∈ s) (hj : j ∈ s)
    (h : f i ⊓ f j ≠ ⊥) : i = j :=
  (hs.elim hi hj) fun hij => h hij.eq_bot
#align set.pairwise_disjoint.elim' Set.PairwiseDisjoint.elim'

theorem PairwiseDisjoint.eq_of_le (hs : s.PairwiseDisjoint f) {i j : ι} (hi : i ∈ s) (hj : j ∈ s)
    (hf : f i ≠ ⊥) (hij : f i ≤ f j) : i = j :=
  (hs.elim' hi hj) fun h => hf <| (inf_of_le_left hij).symm.trans h
#align set.pairwise_disjoint.eq_of_le Set.PairwiseDisjoint.eq_of_le

end SemilatticeInfBot

/-! ### Pairwise disjoint set of sets -/

theorem pairwiseDisjoint_range_singleton :
    (range (singleton : ι → Set ι)).PairwiseDisjoint id := by
  rintro _ ⟨a, rfl⟩ _ ⟨b, rfl⟩ h
  exact disjoint_singleton.2 (ne_of_apply_ne _ h)
#align set.pairwise_disjoint_range_singleton Set.pairwiseDisjoint_range_singleton

theorem pairwiseDisjoint_fiber (f : ι → α) (s : Set α) : s.PairwiseDisjoint fun a => f ⁻¹' {a} :=
  fun _a _ _b _ h => disjoint_iff_inf_le.mpr fun _i ⟨hia, hib⟩ => h <| (Eq.symm hia).trans hib
#align set.pairwise_disjoint_fiber Set.pairwiseDisjoint_fiber

-- classical
theorem PairwiseDisjoint.elim_set {s : Set ι} {f : ι → Set α} (hs : s.PairwiseDisjoint f) {i j : ι}
    (hi : i ∈ s) (hj : j ∈ s) (a : α) (hai : a ∈ f i) (haj : a ∈ f j) : i = j :=
  hs.elim hi hj <| not_disjoint_iff.2 ⟨a, hai, haj⟩
#align set.pairwise_disjoint.elim_set Set.PairwiseDisjoint.elim_set

/-- The partial images of a binary function `f` whose partial evaluations are injective are pairwise
disjoint iff `f` is injective . -/
theorem pairwiseDisjoint_image_right_iff {f : α → β → γ} {s : Set α} {t : Set β}
    (hf : ∀ a ∈ s, Injective (f a)) :
    (s.PairwiseDisjoint fun a => f a '' t) ↔ (s ×ˢ t).InjOn fun p => f p.1 p.2 := by
  refine' ⟨fun hs x hx y hy (h : f _ _ = _) => _, fun hs x hx y hy h => _⟩
  · suffices x.1 = y.1 by exact Prod.ext this (hf _ hx.1 <| h.trans <| by rw [this])
    refine' hs.elim hx.1 hy.1 (not_disjoint_iff.2 ⟨_, mem_image_of_mem _ hx.2, _⟩)
    rw [h]
    exact mem_image_of_mem _ hy.2
  · refine' disjoint_iff_inf_le.mpr _
    rintro _ ⟨⟨a, ha, hab⟩, b, hb, rfl⟩
    exact h (congr_arg Prod.fst <| hs (mk_mem_prod hx ha) (mk_mem_prod hy hb) hab)
#align set.pairwise_disjoint_image_right_iff Set.pairwiseDisjoint_image_right_iff

/-- The partial images of a binary function `f` whose partial evaluations are injective are pairwise
disjoint iff `f` is injective . -/
theorem pairwiseDisjoint_image_left_iff {f : α → β → γ} {s : Set α} {t : Set β}
    (hf : ∀ b ∈ t, Injective fun a => f a b) :
    (t.PairwiseDisjoint fun b => (fun a => f a b) '' s) ↔ (s ×ˢ t).InjOn fun p => f p.1 p.2 := by
  refine' ⟨fun ht x hx y hy (h : f _ _ = _) => _, fun ht x hx y hy h => _⟩
  · suffices x.2 = y.2 by exact Prod.ext (hf _ hx.2 <| h.trans <| by rw [this]) this
    refine' ht.elim hx.2 hy.2 (not_disjoint_iff.2 ⟨_, mem_image_of_mem _ hx.1, _⟩)
    rw [h]
    exact mem_image_of_mem _ hy.1
  · refine' disjoint_iff_inf_le.mpr _
    rintro _ ⟨⟨a, ha, hab⟩, b, hb, rfl⟩
    exact h (congr_arg Prod.snd <| ht (mk_mem_prod ha hx) (mk_mem_prod hb hy) hab)
#align set.pairwise_disjoint_image_left_iff Set.pairwiseDisjoint_image_left_iff

end Set

theorem pairwise_disjoint_fiber (f : ι → α) : Pairwise (Disjoint on fun a : α => f ⁻¹' {a}) :=
  pairwise_univ.1 <| Set.pairwiseDisjoint_fiber f univ
#align pairwise_disjoint_fiber pairwise_disjoint_fiber


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

! This file was ported from Lean 3 source module category_theory.monoidal.preadditive
! leanprover-community/mathlib commit 986c4d5761f938b2e1c43c01f001b6d9d88c2055
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor
import Mathlib.CategoryTheory.Monoidal.Functor

/-!
# Preadditive monoidal categories

A monoidal category is `MonoidalPreadditive` if it is preadditive and tensor product of morphisms
is linear in both factors.
-/

noncomputable section

open Classical

namespace CategoryTheory

open CategoryTheory.Limits

open CategoryTheory.MonoidalCategory

variable (C : Type _) [Category C] [Preadditive C] [MonoidalCategory C]

/-- A category is `MonoidalPreadditive` if tensoring is additive in both factors.

Note we don't `extend Preadditive C` here, as `Abelian C` already extends it,
and we'll need to have both typeclasses sometimes.
-/
class MonoidalPreadditive : Prop where
  /-- tensoring on the right with a zero morphism gives zero -/
  tensor_zero : ∀ {W X Y Z : C} (f : W ⟶ X), f ⊗ (0 : Y ⟶ Z) = 0 := by aesop_cat
  /-- tensoring on the left with a zero morphism gives zero -/
  zero_tensor : ∀ {W X Y Z : C} (f : Y ⟶ Z), (0 : W ⟶ X) ⊗ f = 0 := by aesop_cat
  /-- left tensoring with a morphism is compatible with addition -/
  tensor_add : ∀ {W X Y Z : C} (f : W ⟶ X) (g h : Y ⟶ Z), f ⊗ (g + h) = f ⊗ g + f ⊗ h := by
    aesop_cat
  /-- right tensoring with a morphism is compatible with addition -/
  add_tensor : ∀ {W X Y Z : C} (f g : W ⟶ X) (h : Y ⟶ Z), (f + g) ⊗ h = f ⊗ h + g ⊗ h := by
    aesop_cat
#align category_theory.monoidal_preadditive CategoryTheory.MonoidalPreadditive

--restate_axiom monoidal_preadditive.tensor_zero'
--
--restate_axiom monoidal_preadditive.zero_tensor'
--
--restate_axiom monoidal_preadditive.tensor_add'
--
--restate_axiom monoidal_preadditive.add_tensor'

attribute [simp] MonoidalPreadditive.tensor_zero MonoidalPreadditive.zero_tensor

variable {C}
variable [MonoidalPreadditive C]

attribute [local simp] MonoidalPreadditive.tensor_add MonoidalPreadditive.add_tensor

instance tensorLeft_additive (X : C) : (tensorLeft X).Additive where
#align category_theory.tensor_left_additive CategoryTheory.tensorLeft_additive

instance tensorRight_additive (X : C) : (tensorRight X).Additive where
#align category_theory.tensor_right_additive CategoryTheory.tensorRight_additive

instance tensoringLeft_additive (X : C) : ((tensoringLeft C).obj X).Additive where
#align category_theory.tensoring_left_additive CategoryTheory.tensoringLeft_additive

instance tensoringRight_additive (X : C) : ((tensoringRight C).obj X).Additive where
#align category_theory.tensoring_right_additive CategoryTheory.tensoringRight_additive

/-- A faithful additive monoidal functor to a monoidal preadditive category
ensures that the domain is monoidal preadditive. -/
theorem monoidalPreadditive_of_faithful {D} [Category D] [Preadditive D] [MonoidalCategory D]
    (F : MonoidalFunctor D C) [Faithful F.toFunctor] [F.toFunctor.Additive] :
    MonoidalPreadditive D :=
  { tensor_zero := by
      intros
      apply F.toFunctor.map_injective
      simp [F.map_tensor]
    zero_tensor := by
      intros
      apply F.toFunctor.map_injective
      simp [F.map_tensor]
    tensor_add := by
      intros
      apply F.toFunctor.map_injective
      simp only [F.map_tensor, Functor.map_add, Preadditive.comp_add, Preadditive.add_comp,
        MonoidalPreadditive.tensor_add]
    add_tensor := by
      intros
      apply F.toFunctor.map_injective
      simp only [F.map_tensor, Functor.map_add, Preadditive.comp_add, Preadditive.add_comp,
        MonoidalPreadditive.add_tensor] }
#align category_theory.monoidal_preadditive_of_faithful CategoryTheory.monoidalPreadditive_of_faithful

open BigOperators

theorem tensor_sum {P Q R S : C} {J : Type _} (s : Finset J) (f : P ⟶ Q) (g : J → (R ⟶ S)) :
    (f ⊗ ∑ j in s, g j) = ∑ j in s, f ⊗ g j := by
  rw [← tensor_id_comp_id_tensor]
  let tQ := (((tensoringLeft C).obj Q).mapAddHom : (R ⟶ S) →+ _)
  change _ ≫ tQ _ = _
  rw [tQ.map_sum, Preadditive.comp_sum]
  dsimp [Functor.mapAddHom]
  simp only [tensor_id_comp_id_tensor]
#align category_theory.tensor_sum CategoryTheory.tensor_sum

theorem sum_tensor {P Q R S : C} {J : Type _} (s : Finset J) (f : P ⟶ Q) (g : J → (R ⟶ S)) :
    (∑ j in s, g j) ⊗ f = ∑ j in s, g j ⊗ f := by
  rw [← tensor_id_comp_id_tensor]
  let tQ := (((tensoringRight C).obj P).mapAddHom : (R ⟶ S) →+ _)
  change tQ _ ≫ _ = _
  rw [tQ.map_sum, Preadditive.sum_comp]
  dsimp [Functor.mapAddHom]
  simp only [tensor_id_comp_id_tensor]
#align category_theory.sum_tensor CategoryTheory.sum_tensor

-- In a closed monoidal category, this would hold because
-- `tensorLeft X` is a left adjoint and hence preserves all colimits.
-- In any case it is true in any preadditive category.
instance (X : C) : PreservesFiniteBiproducts (tensorLeft X) where
  preserves {J} :=
    { preserves := fun {f} =>
        { preserves := fun {b} i => isBilimitOfTotal _ (by
            dsimp
            simp only [← tensor_comp, Category.comp_id, ← tensor_sum, ← tensor_id,
              IsBilimit.total i]) } }

instance (X : C) : PreservesFiniteBiproducts (tensorRight X) where
  preserves {J} :=
    { preserves := fun {f} =>
        { preserves := fun {b} i => isBilimitOfTotal _ (by
            dsimp
            simp only [← tensor_comp, Category.comp_id, ← sum_tensor, ← tensor_id,
               IsBilimit.total i]) } }

variable [HasFiniteBiproducts C]

/-- The isomorphism showing how tensor product on the left distributes over direct sums. -/
def leftDistributor {J : Type} [Fintype J] (X : C) (f : J → C) : X ⊗ ⨁ f ≅ ⨁ fun j => X ⊗ f j :=
  (tensorLeft X).mapBiproduct f
#align category_theory.left_distributor CategoryTheory.leftDistributor

@[simp]
theorem leftDistributor_hom {J : Type} [Fintype J] (X : C) (f : J → C) :
    (leftDistributor X f).hom =
      ∑ j : J, (𝟙 X ⊗ biproduct.π f j) ≫ biproduct.ι (fun j => X ⊗ f j) j := by
  ext
  dsimp [leftDistributor, Functor.mapBiproduct, Functor.mapBicone]
  erw [biproduct.lift_π]
  simp only [Preadditive.sum_comp, Category.assoc, biproduct.ι_π, comp_dite, comp_zero,
    Finset.sum_dite_eq', Finset.mem_univ, ite_true, eqToHom_refl, Category.comp_id]
#align category_theory.left_distributor_hom CategoryTheory.leftDistributor_hom

@[simp]
theorem leftDistributor_inv {J : Type} [Fintype J] (X : C) (f : J → C) :
    (leftDistributor X f).inv = ∑ j : J, biproduct.π _ j ≫ (𝟙 X ⊗ biproduct.ι f j) := by
  ext
  dsimp [leftDistributor, Functor.mapBiproduct, Functor.mapBicone]
  simp only [Preadditive.comp_sum, biproduct.ι_π_assoc, dite_comp, zero_comp,
    Finset.sum_dite_eq, Finset.mem_univ, ite_true, eqToHom_refl, Category.id_comp,
    biproduct.ι_desc]
#align category_theory.left_distributor_inv CategoryTheory.leftDistributor_inv

theorem leftDistributor_assoc {J : Type} [Fintype J] (X Y : C) (f : J → C) :
    (asIso (𝟙 X) ⊗ leftDistributor Y f) ≪≫ leftDistributor X _ =
      (α_ X Y (⨁ f)).symm ≪≫ leftDistributor (X ⊗ Y) f ≪≫ biproduct.mapIso fun j => α_ X Y _ := by
  ext
  simp only [Category.comp_id, Category.assoc, eqToHom_refl, Iso.trans_hom, Iso.symm_hom,
    asIso_hom, comp_zero, comp_dite, Preadditive.sum_comp, Preadditive.comp_sum, tensor_sum,
    id_tensor_comp, tensorIso_hom, leftDistributor_hom, biproduct.mapIso_hom, biproduct.ι_map,
    biproduct.ι_π, Finset.sum_dite_irrel, Finset.sum_dite_eq', Finset.sum_const_zero]
  simp only [← id_tensor_comp, biproduct.ι_π]
  simp only [id_tensor_comp, tensor_dite, comp_dite]
  simp only [Category.comp_id, comp_zero, MonoidalPreadditive.tensor_zero, eqToHom_refl,
    tensor_id, if_true, dif_ctx_congr, Finset.sum_congr, Finset.mem_univ, Finset.sum_dite_eq']
  simp only [← tensor_id, associator_naturality, Iso.inv_hom_id_assoc]
#align category_theory.left_distributor_assoc CategoryTheory.leftDistributor_assoc

/-- The isomorphism showing how tensor product on the right distributes over direct sums. -/
def rightDistributor {J : Type} [Fintype J] (X : C) (f : J → C) : (⨁ f) ⊗ X ≅ ⨁ fun j => f j ⊗ X :=
  (tensorRight X).mapBiproduct f
#align category_theory.right_distributor CategoryTheory.rightDistributor

@[simp]
theorem rightDistributor_hom {J : Type} [Fintype J] (X : C) (f : J → C) :
    (rightDistributor X f).hom =
      ∑ j : J, (biproduct.π f j ⊗ 𝟙 X) ≫ biproduct.ι (fun j => f j ⊗ X) j := by
  ext
  dsimp [rightDistributor, Functor.mapBiproduct, Functor.mapBicone]
  erw [biproduct.lift_π]
  simp only [Preadditive.sum_comp, Category.assoc, biproduct.ι_π, comp_dite, comp_zero,
    Finset.sum_dite_eq', Finset.mem_univ, eqToHom_refl, Category.comp_id, ite_true]
#align category_theory.right_distributor_hom CategoryTheory.rightDistributor_hom

@[simp]
theorem rightDistributor_inv {J : Type} [Fintype J] (X : C) (f : J → C) :
    (rightDistributor X f).inv = ∑ j : J, biproduct.π _ j ≫ (biproduct.ι f j ⊗ 𝟙 X) := by
  ext
  dsimp [rightDistributor, Functor.mapBiproduct, Functor.mapBicone]
  simp only [biproduct.ι_desc, Preadditive.comp_sum, ne_eq, biproduct.ι_π_assoc, dite_comp,
    zero_comp, Finset.sum_dite_eq, Finset.mem_univ, eqToHom_refl, Category.id_comp, ite_true]
#align category_theory.right_distributor_inv CategoryTheory.rightDistributor_inv

theorem rightDistributor_assoc {J : Type} [Fintype J] (X Y : C) (f : J → C) :
    (rightDistributor X f ⊗ asIso (𝟙 Y)) ≪≫ rightDistributor Y _ =
      α_ (⨁ f) X Y ≪≫ rightDistributor (X ⊗ Y) f ≪≫ biproduct.mapIso fun j => (α_ _ X Y).symm := by
  ext
  simp only [Category.comp_id, Category.assoc, eqToHom_refl, Iso.symm_hom, Iso.trans_hom,
    asIso_hom, comp_zero, comp_dite, Preadditive.sum_comp, Preadditive.comp_sum, sum_tensor,
    comp_tensor_id, tensorIso_hom, rightDistributor_hom, biproduct.mapIso_hom, biproduct.ι_map,
    biproduct.ι_π, Finset.sum_dite_irrel, Finset.sum_dite_eq', Finset.sum_const_zero,
    Finset.mem_univ, if_true]
  simp only [← comp_tensor_id, biproduct.ι_π, dite_tensor, comp_dite]
  simp only [Category.comp_id, comp_tensor_id, eqToHom_refl, tensor_id, comp_zero,
    MonoidalPreadditive.zero_tensor, if_true, dif_ctx_congr, Finset.mem_univ, Finset.sum_congr,
    Finset.sum_dite_eq']
  simp only [← tensor_id, associator_inv_naturality, Iso.hom_inv_id_assoc]
#align category_theory.right_distributor_assoc CategoryTheory.rightDistributor_assoc

theorem leftDistributor_rightDistributor_assoc {J : Type _} [Fintype J] (X Y : C) (f : J → C) :
    (leftDistributor X f ⊗ asIso (𝟙 Y)) ≪≫ rightDistributor Y _ =
      α_ X (⨁ f) Y ≪≫
        (asIso (𝟙 X) ⊗ rightDistributor Y _) ≪≫
          leftDistributor X _ ≪≫ biproduct.mapIso fun j => (α_ _ _ _).symm := by
  ext
  simp only [Category.comp_id, Category.assoc, eqToHom_refl, Iso.symm_hom, Iso.trans_hom,
    asIso_hom, comp_zero, comp_dite, Preadditive.sum_comp, Preadditive.comp_sum, sum_tensor,
    tensor_sum, comp_tensor_id, tensorIso_hom, leftDistributor_hom, rightDistributor_hom,
    biproduct.mapIso_hom, biproduct.ι_map, biproduct.ι_π, Finset.sum_dite_irrel,
    Finset.sum_dite_eq', Finset.sum_const_zero, Finset.mem_univ, if_true]
  simp only [← comp_tensor_id, ← id_tensor_comp_assoc, Category.assoc, biproduct.ι_π, comp_dite,
    dite_comp, tensor_dite, dite_tensor]
  simp only [Category.comp_id, Category.id_comp, Category.assoc, id_tensor_comp, comp_zero,
    zero_comp, MonoidalPreadditive.tensor_zero, MonoidalPreadditive.zero_tensor, comp_tensor_id,
    eqToHom_refl, tensor_id, if_true, dif_ctx_congr, Finset.sum_congr, Finset.mem_univ,
    Finset.sum_dite_eq']
  simp only [associator_inv_naturality, Iso.hom_inv_id_assoc]
#align category_theory.left_distributor_right_distributor_assoc CategoryTheory.leftDistributor_rightDistributor_assoc

end CategoryTheory