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

! This file was ported from Lean 3 source module data.real.enat_ennreal
! leanprover-community/mathlib commit 53b216bcc1146df1c4a0a86877890ea9f1f01589
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.ENat.Basic
import Mathlib.Data.Real.ENNReal

/-!
# Coercion from `ℕ∞` to `ℝ≥0∞`

In this file we define a coercion from `ℕ∞` to `ℝ≥0∞` and prove some basic lemmas about this map.
-/

open Classical NNReal ENNReal

noncomputable section

namespace ENat

variable {m n : ℕ∞}

/-- Coercion from `ℕ∞` to `ℝ≥0∞`. -/
@[coe] def toENNReal : ℕ∞ → ℝ≥0∞ := WithTop.map Nat.cast

instance hasCoeENNReal : CoeTC ℕ∞ ℝ≥0∞ := ⟨toENNReal⟩
#align enat.has_coe_ennreal ENat.hasCoeENNReal

@[simp]
theorem map_coe_nnreal : WithTop.map ((↑) : ℕ → ℝ≥0) = ((↑) : ℕ∞ → ℝ≥0∞) :=
  rfl
#align enat.map_coe_nnreal ENat.map_coe_nnreal

/-- Coercion `ℕ∞ → ℝ≥0∞` as an `order_embedding`. -/
@[simps! (config := { fullyApplied := false })]
def toENNRealOrderEmbedding : ℕ∞ ↪o ℝ≥0∞ :=
  Nat.castOrderEmbedding.withTopMap
#align enat.to_ennreal_order_embedding ENat.toENNRealOrderEmbedding

/-- Coercion `ℕ∞ → ℝ≥0∞` as a ring homomorphism. -/
@[simps! (config := { fullyApplied := false })]
def toENNRealRingHom : ℕ∞ →+* ℝ≥0∞ :=
  .withTopMap (Nat.castRingHom ℝ≥0) Nat.cast_injective
#align enat.to_ennreal_ring_hom ENat.toENNRealRingHom

@[simp, norm_cast]
theorem toENNReal_top : ((⊤ : ℕ∞) : ℝ≥0∞) = ⊤ :=
  rfl
#align enat.coe_ennreal_top ENat.toENNReal_top

@[simp, norm_cast]
theorem toENNReal_coe (n : ℕ) : ((n : ℕ∞) : ℝ≥0∞) = n :=
  rfl
#align enat.coe_ennreal_coe ENat.toENNReal_coe

@[simp, norm_cast]
theorem toENNReal_ofNat (n : ℕ) [n.AtLeastTwo] : ((OfNat.ofNat n : ℕ∞) : ℝ≥0∞) = OfNat.ofNat n :=
  rfl

@[simp, norm_cast]
theorem toENNReal_le : (m : ℝ≥0∞) ≤ n ↔ m ≤ n :=
  toENNRealOrderEmbedding.le_iff_le
#align enat.coe_ennreal_le ENat.toENNReal_le

@[simp, norm_cast]
theorem toENNReal_lt : (m : ℝ≥0∞) < n ↔ m < n :=
  toENNRealOrderEmbedding.lt_iff_lt
#align enat.coe_ennreal_lt ENat.toENNReal_lt

@[mono]
theorem toENNReal_mono : Monotone ((↑) : ℕ∞ → ℝ≥0∞) :=
  toENNRealOrderEmbedding.monotone
#align enat.coe_ennreal_mono ENat.toENNReal_mono

@[mono]
theorem toENNReal_strictMono : StrictMono ((↑) : ℕ∞ → ℝ≥0∞) :=
  toENNRealOrderEmbedding.strictMono
#align enat.coe_ennreal_strict_mono ENat.toENNReal_strictMono

@[simp, norm_cast]
theorem toENNReal_zero : ((0 : ℕ∞) : ℝ≥0∞) = 0 :=
  map_zero toENNRealRingHom
#align enat.coe_ennreal_zero ENat.toENNReal_zero

@[simp]
theorem toENNReal_add (m n : ℕ∞) : ↑(m + n) = (m + n : ℝ≥0∞) :=
  map_add toENNRealRingHom m n
#align enat.coe_ennreal_add ENat.toENNReal_add

@[simp]
theorem toENNReal_one : ((1 : ℕ∞) : ℝ≥0∞) = 1 :=
  map_one toENNRealRingHom
#align enat.coe_ennreal_one ENat.toENNReal_one

#noalign enat.coe_ennreal_bit0
#noalign enat.coe_ennreal_bit1

@[simp]
theorem toENNReal_mul (m n : ℕ∞) : ↑(m * n) = (m * n : ℝ≥0∞) :=
  map_mul toENNRealRingHom m n
#align enat.coe_ennreal_mul ENat.toENNReal_mul

@[simp]
theorem toENNReal_min (m n : ℕ∞) : ↑(min m n) = (min m n : ℝ≥0∞) :=
  toENNReal_mono.map_min
#align enat.coe_ennreal_min ENat.toENNReal_min

@[simp]
theorem toENNReal_max (m n : ℕ∞) : ↑(max m n) = (max m n : ℝ≥0∞) :=
  toENNReal_mono.map_max
#align enat.coe_ennreal_max ENat.toENNReal_max

@[simp]
theorem toENNReal_sub (m n : ℕ∞) : ↑(m - n) = (m - n : ℝ≥0∞) :=
  WithTop.map_sub Nat.cast_tsub Nat.cast_zero m n
#align enat.coe_ennreal_sub ENat.toENNReal_sub

end ENat


import algebraic_topology.simplicial_set

open category_theory
open category_theory.limits
open simplex_category
open sSet
open_locale simplicial

/-! # Simplex as hom
Defines the functions simplex as hom. This is the function that turns a simplex into a morphism
from a standard simplex to the simplicial set.
-/

namespace simplex_category

@[simps] def last (n : simplex_category) := fin.last n.len

def op (n : simplex_category) := opposite.op n

end simplex_category

namespace sSet

/-- Interpret a simplex as a morphism from a standard simplicial set. -/
def simplex_as_hom {n} {X : sSet} (x : X.obj (opposite.op n)) :
  standard_simplex.obj n ⟶ X :=
{ app := λ m f,
  begin
    change m.unop ⟶ n at f,
    exact X.map f.op x,
  end,
  naturality' := λ k m f,
  begin
    ext1 g, change k.unop ⟶ n at g, simp,
    rw [←types_comp_apply (X.map g.op) (X.map f), ←X.map_comp],
    refl,
  end
}

lemma simplex_as_hom_id {n} {X : sSet} (x : X.obj (opposite.op n)) :
  (simplex_as_hom x).app (opposite.op n) (𝟙 n) = x :=
begin
  change X.map (𝟙 n).op x = x,
  rw [op_id, X.map_id],
  refl,
end

lemma simplex_as_hom_eq_iff {n} {X : sSet} (x y : X.obj (opposite.op n)) :
  simplex_as_hom x = simplex_as_hom y ↔ x = y :=
begin
  split,
  { intro h, rwa [←simplex_as_hom_id x, ←simplex_as_hom_id y, h] },
  { intro h, rwa h }
end

/-- Interpret a morphism in the simplex category as a morphism between standard simplicial sets. -/
def to_sSet_hom {n m} (f : [n] ⟶ [m]) : Δ[n] ⟶ Δ[m] := sSet.standard_simplex.map f

@[simp]
lemma to_sSet_hom_id {n} : to_sSet_hom (𝟙 [n]) = 𝟙 Δ[n] := yoneda.map_id [n]

lemma hom_comp_simplex_as_hom {n m} (f : [n] ⟶ [m]) {X : sSet} (x : X.obj (opposite.op [m])) :
  to_sSet_hom f ≫ simplex_as_hom x = simplex_as_hom (X.map f.op x) :=
begin
  ext k g, change k.unop ⟶ [n] at g,
  simp [to_sSet_hom, simplex_as_hom],
  rw [←types_comp_apply (X.map f.op) (X.map g.op) x, ←X.map_comp],
  refl,
end

lemma simplex_as_hom_comp_hom {n} {X Y : sSet} (x : X.obj (opposite.op n)) (f : X ⟶ Y) :
  simplex_as_hom x ≫ f = simplex_as_hom (f.app (opposite.op n) x) :=
begin
  ext k g, change k.unop ⟶ n at g,
  simp [to_sSet_hom, simplex_as_hom],
  rw [←types_comp_apply (X.map g.op) (f.app k) x, ←types_comp_apply (f.app _) (Y.map g.op) x],
  apply congr_fun,
  apply f.naturality,
end

end sSet


/-
Copyright (c) 2018 Mario Carneiro, Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kevin Buzzard
-/
import algebra.ring.idempotents
import ring_theory.finiteness

/-!
## Lemmas on idempotent finitely generated ideals

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

namespace ideal

/-- A finitely generated idempotent ideal is generated by an idempotent element -/
lemma is_idempotent_elem_iff_of_fg {R : Type*} [comm_ring R] (I : ideal R)
  (h : I.fg) :
  is_idempotent_elem I ↔ ∃ e : R, is_idempotent_elem e ∧ I = R ∙ e :=
begin
  split,
  { intro e,
    obtain ⟨r, hr, hr'⟩ := submodule.exists_mem_and_smul_eq_self_of_fg_of_le_smul I I h
      (by { rw [smul_eq_mul], exact e.ge }),
    simp_rw smul_eq_mul at hr',
    refine ⟨r, hr' r hr, antisymm _ ((submodule.span_singleton_le_iff_mem _ _).mpr hr)⟩,
    intros x hx,
    rw ← hr' x hx,
    exact ideal.mem_span_singleton'.mpr ⟨_, mul_comm _ _⟩ },
  { rintros ⟨e, he, rfl⟩,
    simp [is_idempotent_elem, ideal.span_singleton_mul_span_singleton, he.eq] }
end

lemma is_idempotent_elem_iff_eq_bot_or_top {R : Type*} [comm_ring R] [is_domain R]
  (I : ideal R) (h : I.fg) :
  is_idempotent_elem I ↔ I = ⊥ ∨ I = ⊤ :=
begin
  split,
  { intro H,
    obtain ⟨e, he, rfl⟩ := (I.is_idempotent_elem_iff_of_fg h).mp H,
    simp only [ideal.submodule_span_eq, ideal.span_singleton_eq_bot],
    apply or_of_or_of_imp_of_imp (is_idempotent_elem.iff_eq_zero_or_one.mp he) id,
    rintro rfl,
    simp },
  { rintro (rfl|rfl); simp [is_idempotent_elem] }
end

end ideal


import tactic

/-
# Level 6 : Or commutes
-/

namespace math3345 -- hide

/-
P and Q are propositions.
-/

variable P : Prop
variable Q : Prop

/- Lemma : no-side-bar

`h` is evidence that `P ∨ Q` but we don't know which one holds.  To
use the information provided by `h`, start with `cases h` to consider
the two cases (that `P` holds or that `Q` holds) separately.

Then use `left` and `right` and `exact` to finish the proof.
-/
lemma using_disjunction (h : (P ∨ Q)) : (Q ∨ P) :=
begin
  sorry,

end

end math3345 -- hide


import .love05_inductive_predicates_demo

/-! # LoVe Demo 11: Logical Foundations of Mathematics

We dive deeper into the logical foundations of Lean. Most of the features
described here are especially relevant for defining mathematical objects and
proving theorems about them. -/

set_option pp.beta true

namespace LoVe

/-! ## Type Universes

Not only terms have a type, but also types have a type. For example, by the
Curry-Howard correspondence:

    `@and.intro : ∀a b, a → b → a ∧ b`

Moreover:

    `∀a b, a → b → a ∧ b : Prop`

Now, what is the type of `Prop`? `Prop` has the same type as virtually all other
types we have constructed so far:

    `Prop : Type`

What is the type of `Type`? The typing `Type : Type` would lead to a
contradiction, called **Girard's paradox**, resembling Russel's paradox.
Instead:

    `Type   : Type 1`
    `Type 1 : Type 2`
    `Type 2 : Type 3`
    ⋮

Aliases:

    `Type`   := `Type 0`
    `Prop`   := `Sort 0`
    `Type u` := `Sort (u + 1)`

The types of types (`Sort u`, `Type u`, and `Prop`) are called (__type__)
__universes__. The `u` in `Sort u` is a __universe level__.

The hierarchy is captured by the following typing judgment:

    ————————————————————————— Sort
    C ⊢ Sort u : Sort (u + 1) -/

#check @and.intro
#check ∀a b : Prop, a → b → a ∧ b
#check Prop
#check ℕ
#check Type
#check Type 1
#check Type 2

universe variables u v

#check Type u

#check Sort 0
#check Sort 1
#check Sort 2
#check Sort u

#check Type*

/-! ## The Peculiarities of Prop

`Prop` is different from the other type universes in many respects.

### Impredicativity

The function type `σ → τ` is put into the larger one of the type universes that
`σ` and `τ` live in:

    C ⊢ σ : Type u    C ⊢ τ : Type v
    ————————————————————————————————— Arrow-Type
    C ⊢ σ → τ : Type (max u v)

For dependent types, this generalizes to

    C ⊢ σ : Type u    C, x : σ ⊢ τ[x] : Type v
    ——————————————————————————————————————————— Forall-Type
    C ⊢ (∀x : σ, τ[x]) : Type (max u v)

This behavior of the universes `Type v` is called __predicativity__.

To force expressions such as `∀a : Prop, a → a` to be of type `Prop` anyway, we
need a special typing rule for `Prop`:

    C ⊢ σ : Sort u    x : σ ⊢ τ[x] : Prop
    —————————————————————————————————————— Forall-Prop
    C ⊢ (∀x : σ, τ[x]) : Prop

This behavior of `Prop` is called __impredicativity__.

The rules `Forall-Type` and `Forall-Prop` can be generalized into a single rule:

    C ⊢ σ : Sort u    C, x : σ ⊢ τ[x] : Sort v
    ——————————————————————————————————————————— Forall
    C ⊢ (∀x : σ, τ[x]) : Sort (imax u v)

where

    `imax u 0       = 0`
    `imax u (v + 1) = max u (v + 1)` -/

#check λ(α : Type u) (β : Type v), α → β
#check ∀a : Prop, a → a

/-! ### Proof Irrelevance

A second difference between `Prop` and `Type u` is __proof irrelevance__:

    `∀(a : Prop) (h₁ h₂ : a), h₁ = h₂`

This and makes reasoning about dependent types easier.

When viewing a proposition as a type and a proof as an element of that type,
proof irrelevance means that a proposition is either an empty type or has
exactly one inhabitant.

Proof irrelevance can be proved `by refl`.

An unfortunate consequence of proof irrelevance is that it prevents us from
performing rule induction by pattern matching. -/

#check proof_irrel

lemma proof_irrel {a : Prop} (h₁ h₂ : a) :
  h₁ = h₂ :=
by refl

/-! ### No Large Elimination

A further difference between `Prop` and `Type u` is that `Prop` does not allow
__large elimination__, meaning that it impossible to extract data from a proof
of a proposition.

This is necessary to allow proof irrelevance. -/

#check 0

-- fails
def unsquare (i : ℤ) (hsq : ∃j, i = j * j) : ℤ :=
match hsq with
| Exists.intro j _ := j
end

/-! If the above were accepted, we could derive `false` as follows.

Let

    `hsq₁` := `Exists.intro 3 dec_trivial`
    `hsq₂` := `Exists.intro (-3) dec_trivial`

be two proofs of `∃j, (9 : ℤ) = j * j`. Then

    `unsquare 9 hsq₁ = 3`
    `unsquare 9 hsq₂ = -3`

However, by proof irrelevance, `hsq₁ = hsq₂`. Hence

    `unsquare 9 hsq₂ = 3`

Thus

    `3 = -3`

a contradiction.

As a compromise, Lean allows __small elimination__. It is called small
elimination because it eliminate only into `Prop`, whereas large elimination can
eliminate into an arbitrary large type universe `Sort u`. This means we can use
`match` to analyze the structure of a proof, extract existential witnesses, and
so on, as long as the `match` expression is itself a proof. We have seen several
examples of this in lecture 5.

As a further compromise, Lean allows large elimination for
__syntactic subsingletons__: types in `Prop` for which it can be established
syntactically that they have cardinality 0 or 1. These are propositions such as
`false` and `a ∧ b` that can be proved in at most one way.

## The Axiom of Choice

Lean's logic includes the axiom of choice, which makes it possible to obtain an
arbitrary element from any nonempty type.

Consider Lean's `nonempty` inductive predicate: -/

#print nonempty

/-! The predicate states that `α` has at least one element.

To prove `nonempty α`, we must provide an `α` value to `nonempty.intro`: -/

lemma nat.nonempty :
  nonempty ℕ :=
nonempty.intro 0

/-! Since `nonempty` lives in `Prop`, large elimination is not available, and
thus we cannot extract the element that was used from a proof of `nonempty α`.

The axiom of choice allows us to obtain some element of type `α` if we can show
`nonempty α`: -/

#print classical.choice

/-! It will just give us an arbitrary element of `α`; we have no way of knowing
whether this is the element that was used to prove `nonempty α`.

The constant `classical.choice` is noncomputable, one of the reasons why some
logicians prefer to work without this axiom. -/

#eval classical.choice nat.nonempty     -- fails
#reduce classical.choice nat.nonempty   -- result: classical.choice _

/-! The axiom of choice is only an axiom in Lean's core library, giving users
the freedom to work with or without it.

Definitions using it must be marked as `noncomputable`: -/

noncomputable def arbitrary_nat : ℕ :=
classical.choice nat.nonempty

/-! The following tools rely on choice.

### Law of Excluded Middle -/

#check classical.em -- excluded middle
#check classical.by_contradiction

/-! ### Hilbert Choice -/

#print classical.some
#print classical.some_spec

#check λ(p : ℕ → Prop) (h : ∃n : ℕ, p n), classical.some h
#check λ(p : ℕ → Prop) (h : ∃n : ℕ, p n), classical.some_spec h

/-! ### Set-Theoretic Axiom of Choice -/

#print classical.axiom_of_choice

/-! ## Subtypes

Subtyping is a mechanism to create new types from existing ones.

Given a predicate on the elements of the base type, the __subtype__ contains
only those elements of the base type that fulfill this property. More precisely,
the subtype contains element–proof pairs that combine an element of the base
type and a proof that it fulfills the property.

Subtyping is useful for those types that cannot be defined as an inductive
type. For example, any attempt at defining the type of finite sets along the
following lines is doomed to fail: -/

inductive finset (α : Type) : Type
| empty  : finset
| insert : α → finset → finset

/-! Given a base type and a property, the subtype has the syntax

    `{` _variable_ `:` _base-type_ `//` _property-applied-to-variable_ `}`

Example:

    `{i : ℕ // i ≤ n}`

Alias:

    `{x : τ // P[x]}` := `@subtype τ (λx, P[x])`

### First Example: Full Binary Trees -/

#check btree
#check is_full
#check mirror
#check is_full_mirror
#check mirror_mirror

def full_btree (α : Type) : Type :=
{t : btree α // is_full t}

#print subtype
#check subtype.mk

/-! To define elements of `full_btree`, we must provide a `btree` and a proof
that it is full: -/

def empty_full_btree : full_btree ℕ :=
subtype.mk btree.empty is_full.empty

def full_btree_6 : full_btree ℕ :=
subtype.mk (btree.node 6 btree.empty btree.empty)
  begin
    apply is_full.node,
    apply is_full.empty,
    apply is_full.empty,
    refl
  end

#reduce subtype.val full_btree_6
#check subtype.property full_btree_6

/-! We can lift existing operations on `btree` to `full_btree`: -/

def full_btree.mirror {α : Type} (t : full_btree α) :
  full_btree α :=
subtype.mk (mirror (subtype.val t))
  begin
    apply is_full_mirror,
    apply subtype.property t
  end

#reduce subtype.val (full_btree.mirror full_btree_6)

/-! And of course we can prove lemmas about the lifted operations: -/

lemma full_btree.mirror_mirror {α : Type} (t : full_btree α) :
  (full_btree.mirror (full_btree.mirror t)) = t :=
begin
  apply subtype.eq,
  simp [full_btree.mirror],
  apply mirror_mirror
end

#check subtype.eq

/-! ### Second Example: Vectors -/

#check vector

def vector (α : Type) (n : ℕ) : Type :=
{xs : list α // list.length xs = n}

def vector_123 : vector ℤ 3 :=
subtype.mk [1, 2, 3] (by refl)

def vector.neg {n : ℕ} (v : vector ℤ n) : vector ℤ n :=
subtype.mk (list.map int.neg (subtype.val v))
  begin
    rewrite list.length_map,
    exact subtype.property v
  end

lemma vector.neg_neg (n : ℕ) (v : vector ℤ n) :
  vector.neg (vector.neg v) = v :=
begin
  apply subtype.eq,
  simp [vector.neg]
end

#check finset

/-! ## Quotient Types

Quotients are a powerful construction in mathematics used to construct `ℤ`, `ℚ`,
`ℝ`, and many other types.

Like subtyping, quotienting constructs a new type from an existing type. Unlike
a subtype, a quotient type contains all of the elements of the base type, but
some elements that were different in the base type are considered equal in the
quotient type. In mathematical terms, the quotient type is isomorphic to a
partition of the base type.

To define a quotient type, we need to provide a type that it is derived from and
a equivalence relation on it that determines which elements are considered
equal. -/

#check quotient
#print setoid

#check quotient.mk
#check quotient.sound
#check quotient.exact

#check quotient.lift
#check quotient.lift₂
#check quotient.induction_on

/-! ## First Example: Integers

Let us build the integers `ℤ` as a quotient over pairs of natural numbers
`ℕ × ℕ`.

A pair `(p, n)` of natural numbers represents the integer `p - n`. Nonnegative
integers `p` can be represented by `(p, 0)`. Negative integers `-n` can be
represented by `(0, n)`. However, many representations of the same integer are
possible; e.g., `(7, 0)`, `(8, 1)`, `(9, 2)`, and `(10, 3)` all represent the
integer `7`.

Which equivalence relation can we use?

We want two pairs `(p₁, n₁)` and `(p₂, n₂)` to be equal if `p₁ - n₁ = p₂ - n₂`.
However, this does not work because subtraction on `ℕ` is ill-behaved (e.g.,
`0 - 1 = 0`). Instead, we use `p₁ + n₂ = p₂ + n₁`. -/

@[instance] def int.rel : setoid (ℕ × ℕ) :=
{ r     :=
    λpn₁ pn₂ : ℕ × ℕ,
      prod.fst pn₁ + prod.snd pn₂ = prod.fst pn₂ + prod.snd pn₁,
  iseqv :=
    begin
      repeat { apply and.intro },
      { intro pn,
        refl },
      { intros pn₁ pn₂ h,
        rewrite h },
      { intros pn₁ pn₂ pn₃ h₁₂ h₂₃,
        apply @eq_of_add_eq_add_right _ _ _ (prod.snd pn₂),
        cc }
    end }

#print equivalence

lemma int.rel_iff (pn₁ pn₂ : ℕ × ℕ) :
  pn₁ ≈ pn₂ ↔
  prod.fst pn₁ + prod.snd pn₂ = prod.fst pn₂ + prod.snd pn₁ :=
by refl

def int : Type :=
quotient int.rel

def int.zero : int :=
⟦(0, 0)⟧

lemma int.zero_eq (m : ℕ) :
  int.zero = ⟦(m, m)⟧ :=
begin
  rewrite int.zero,
  apply quotient.sound,
  rewrite int.rel_iff,
  simp
end

def int.add : int → int → int :=
quotient.lift₂
  (λpn₁ pn₂ : ℕ × ℕ,
     ⟦(prod.fst pn₁ + prod.fst pn₂,
       prod.snd pn₁ + prod.snd pn₂)⟧)
  begin
    intros pn₁ pn₂ pn₁' pn₂' h₁ h₂,
    apply quotient.sound,
    rewrite int.rel_iff at *,
    linarith
  end

lemma int.add_eq (p₁ n₁ p₂ n₂ : ℕ) :
  int.add ⟦(p₁, n₁)⟧ ⟦(p₂, n₂)⟧ = ⟦(p₁ + p₂, n₁ + n₂)⟧ :=
by refl

lemma int.add_zero (i : int) :
  int.add int.zero i = i :=
begin
  apply quotient.induction_on i,
  intro pn,
  rewrite int.zero,
  cases pn with p n,
  rewrite int.add_eq,
  apply quotient.sound,
  simp
end

/-! This definitional syntax would be nice: -/

-- fails
def int.add : int → int → int
| ⟦(p₁, n₁)⟧ ⟦(p₂, n₂)⟧ := ⟦(p₁ + p₂, n₁ + n₂)⟧

/-! But it would be dangerous: -/

-- fails
def int.fst : int → ℕ
| ⟦(p, n)⟧ := p

/-! Using `int.fst`, we could derive `false`. First, we have

    `int.fst ⟦(0, 0)⟧ = 0`
    `int.fst ⟦(1, 1)⟧ = 1`

But since `⟦(0, 0)⟧ = ⟦(1, 1)⟧`, we get

    `0 = 1` -/

/-! ### Second Example: Unordered Pairs

__Unordered pairs__ are pairs for which no distinction is made between the first
and second components. They are usually written `{a, b}`.

We will introduce the type `upair` of unordered pairs as the quotient of pairs
`(a, b)` with respect to the relation "contains the same elements as". -/

@[instance] def upair.rel (α : Type) : setoid (α × α) :=
{ r     := λab₁ ab₂ : α × α,
    ({prod.fst ab₁, prod.snd ab₁} : set α) =
    ({prod.fst ab₂, prod.snd ab₂} : set α),
  iseqv := by repeat { apply and.intro }; finish }

-- downprioritizes `upair.rel` w.r.t. `int.rel`
attribute [instance, priority 999] upair.rel

lemma upair.rel_iff {α : Type} (ab₁ ab₂ : α × α) :
  ab₁ ≈ ab₂ ↔
  ({prod.fst ab₁, prod.snd ab₁} : set α) =
  ({prod.fst ab₂, prod.snd ab₂} : set α) :=
by refl

def upair (α : Type) : Type :=
quotient (upair.rel α)

def upair.mk {α : Type} (a b : α) : upair α :=
⟦(a, b)⟧

/-! It is easy to prove that the `upair.mk` constructor is symmetric: -/

lemma upair.mk_symm {α : Type} (a b : α) :
  upair.mk a b = upair.mk b a :=
begin
  unfold upair.mk,
  apply quotient.sound,
  rewrite upair.rel_iff,
  apply set.insert_comm
end

/-! Another representation of unordered pairs is as sets of cardinality 1 or 2.
The following operation converts `upair` to that representation: -/

def set_of_upair {α : Type} : upair α → set α :=
quotient.lift (λab : α × α, {prod.fst ab, prod.snd ab})
  begin
    intros ab₁ ab₂ h,
    rewrite upair.rel_iff at *,
    exact h
  end

end LoVe


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

! This file was ported from Lean 3 source module linear_algebra.tensor_algebra.to_tensor_power
! leanprover-community/mathlib commit d97a0c9f7a7efe6d76d652c5a6b7c9c634b70e0a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.TensorAlgebra.Basic
import Mathbin.LinearAlgebra.TensorPower

/-!
# Tensor algebras as direct sums of tensor powers

In this file we show that `tensor_algebra R M` is isomorphic to a direct sum of tensor powers, as
`tensor_algebra.equiv_direct_sum`.
-/

open DirectSum TensorProduct

variable {R M : Type _} [CommSemiring R] [AddCommMonoid M] [Module R M]

namespace TensorPower

/-- The canonical embedding from a tensor power to the tensor algebra -/
def toTensorAlgebra {n} : (⨂[R]^n) M →ₗ[R] TensorAlgebra R M :=
  PiTensorProduct.lift (TensorAlgebra.tprod R M n)
#align tensor_power.to_tensor_algebra TensorPower.toTensorAlgebra

@[simp]
theorem toTensorAlgebra_tprod {n} (x : Fin n → M) :
    TensorPower.toTensorAlgebra (PiTensorProduct.tprod R x) = TensorAlgebra.tprod R M n x :=
  PiTensorProduct.lift.tprod _
#align tensor_power.to_tensor_algebra_tprod TensorPower.toTensorAlgebra_tprod

@[simp]
theorem toTensorAlgebra_ghasOne :
    (@GradedMonoid.GOne.one _ (fun n => (⨂[R]^n) M) _ _).toTensorAlgebra = 1 :=
  TensorPower.toTensorAlgebra_tprod _
#align tensor_power.to_tensor_algebra_ghas_one TensorPower.toTensorAlgebra_ghasOne

@[simp]
theorem toTensorAlgebra_ghasMul {i j} (a : (⨂[R]^i) M) (b : (⨂[R]^j) M) :
    (@GradedMonoid.GMul.mul _ (fun n => (⨂[R]^n) M) _ _ _ _ a b).toTensorAlgebra =
      a.toTensorAlgebra * b.toTensorAlgebra :=
  by
  -- change `a` and `b` to `tprod R a` and `tprod R b`
  rw [TensorPower.ghasMul_eq_coe_linearMap, ← LinearMap.compr₂_apply, ← @LinearMap.mul_apply' R, ←
    LinearMap.compl₂_apply, ← LinearMap.comp_apply]
  refine' LinearMap.congr_fun (LinearMap.congr_fun _ a) b
  clear a b
  ext (a b)
  simp only [LinearMap.compr₂_apply, LinearMap.mul_apply', LinearMap.compl₂_apply,
    LinearMap.comp_apply, LinearMap.compMultilinearMap_apply, PiTensorProduct.lift.tprod,
    TensorPower.tprod_mul_tprod, TensorPower.toTensorAlgebra_tprod, TensorAlgebra.tprod_apply, ←
    ghas_mul_eq_coe_linear_map]
  refine' Eq.trans _ List.prod_append
  congr
  rw [← List.map_ofFn _ (TensorAlgebra.ι R), ← List.map_ofFn _ (TensorAlgebra.ι R), ←
    List.map_ofFn _ (TensorAlgebra.ι R), ← List.map_append, List.ofFn_fin_append]
#align tensor_power.to_tensor_algebra_ghas_mul TensorPower.toTensorAlgebra_ghasMul

@[simp]
theorem toTensorAlgebra_galgebra_toFun (r : R) :
    (@DirectSum.Galgebra.toFun _ R (fun n => (⨂[R]^n) M) _ _ _ _ _ _ _ r).toTensorAlgebra =
      algebraMap _ _ r :=
  by
  rw [TensorPower.galgebra_toFun_def, TensorPower.algebraMap₀_eq_smul_one, LinearMap.map_smul,
    TensorPower.toTensorAlgebra_ghasOne, Algebra.algebraMap_eq_smul_one]
#align tensor_power.to_tensor_algebra_galgebra_to_fun TensorPower.toTensorAlgebra_galgebra_toFun

end TensorPower

namespace TensorAlgebra

/-- The canonical map from a direct sum of tensor powers to the tensor algebra. -/
def ofDirectSum : (⨁ n, (⨂[R]^n) M) →ₐ[R] TensorAlgebra R M :=
  DirectSum.toAlgebra _ _ (fun n => TensorPower.toTensorAlgebra) TensorPower.toTensorAlgebra_ghasOne
    (fun i j => TensorPower.toTensorAlgebra_ghasMul) TensorPower.toTensorAlgebra_galgebra_toFun
#align tensor_algebra.of_direct_sum TensorAlgebra.ofDirectSum

@[simp]
theorem ofDirectSum_of_tprod {n} (x : Fin n → M) :
    ofDirectSum (DirectSum.of _ n (PiTensorProduct.tprod R x)) = tprod R M n x :=
  (DirectSum.toAddMonoid_of _ _ _).trans (TensorPower.toTensorAlgebra_tprod _)
#align tensor_algebra.of_direct_sum_of_tprod TensorAlgebra.ofDirectSum_of_tprod

/-- The canonical map from the tensor algebra to a direct sum of tensor powers. -/
def toDirectSum : TensorAlgebra R M →ₐ[R] ⨁ n, (⨂[R]^n) M :=
  TensorAlgebra.lift R <|
    DirectSum.lof R ℕ (fun n => (⨂[R]^n) M) _ ∘ₗ
      (LinearEquiv.symm <| PiTensorProduct.subsingletonEquiv (0 : Fin 1) : M ≃ₗ[R] _).toLinearMap
#align tensor_algebra.to_direct_sum TensorAlgebra.toDirectSum

@[simp]
theorem toDirectSum_ι (x : M) :
    toDirectSum (ι R x) =
      DirectSum.of (fun n => (⨂[R]^n) M) _ (PiTensorProduct.tprod R fun _ : Fin 1 => x) :=
  TensorAlgebra.lift_ι_apply _ _
#align tensor_algebra.to_direct_sum_ι TensorAlgebra.toDirectSum_ι

theorem ofDirectSum_comp_toDirectSum :
    ofDirectSum.comp toDirectSum = AlgHom.id R (TensorAlgebra R M) :=
  by
  ext
  simp [DirectSum.lof_eq_of, tprod_apply]
#align tensor_algebra.of_direct_sum_comp_to_direct_sum TensorAlgebra.ofDirectSum_comp_toDirectSum

@[simp]
theorem ofDirectSum_toDirectSum (x : TensorAlgebra R M) : ofDirectSum x.toDirectSum = x :=
  AlgHom.congr_fun ofDirectSum_comp_toDirectSum x
#align tensor_algebra.of_direct_sum_to_direct_sum TensorAlgebra.ofDirectSum_toDirectSum

@[simp]
theorem mk_reindex_cast {n m : ℕ} (h : n = m) (x : (⨂[R]^n) M) :
    GradedMonoid.mk m (PiTensorProduct.reindex R M (Equiv.cast <| congr_arg Fin h) x) =
      GradedMonoid.mk n x :=
  Eq.symm (PiTensorProduct.gradedMonoid_eq_of_reindex_cast h rfl)
#align tensor_algebra.mk_reindex_cast TensorAlgebra.mk_reindex_cast

@[simp]
theorem mk_reindex_fin_cast {n m : ℕ} (h : n = m) (x : (⨂[R]^n) M) :
    GradedMonoid.mk m (PiTensorProduct.reindex R M (Fin.cast h).toEquiv x) = GradedMonoid.mk n x :=
  by rw [Fin.cast_to_equiv, mk_reindex_cast h]
#align tensor_algebra.mk_reindex_fin_cast TensorAlgebra.mk_reindex_fin_cast

/-- The product of tensor products made of a single vector is the same as a single product of
all the vectors. -/
theorem TensorPower.list_prod_gradedMonoid_mk_single (n : ℕ) (x : Fin n → M) :
    ((List.finRange n).map fun a =>
          (GradedMonoid.mk _ (PiTensorProduct.tprod R fun i : Fin 1 => x a) :
            GradedMonoid fun n => (⨂[R]^n) M)).Prod =
      GradedMonoid.mk n (PiTensorProduct.tprod R x) :=
  by
  refine' Fin.consInduction _ _ x <;> clear x
  · rw [List.finRange_zero, List.map_nil, List.prod_nil]
    rfl
  · intro n x₀ x ih
    rw [List.finRange_succ_eq_map, List.map_cons, List.prod_cons, List.map_map, Function.comp]
    simp_rw [Fin.cons_zero, Fin.cons_succ]
    rw [ih, GradedMonoid.mk_mul_mk, TensorPower.tprod_mul_tprod]
    refine' TensorPower.gradedMonoid_eq_of_cast (add_comm _ _) _
    dsimp only [GradedMonoid.mk]
    rw [TensorPower.cast_tprod]
    simp_rw [Fin.append_left_eq_cons, Function.comp]
    congr 1 with i
    congr 1
    rw [Fin.cast_trans, Fin.cast_refl, OrderIso.refl_apply]
#align tensor_power.list_prod_graded_monoid_mk_single TensorPower.list_prod_gradedMonoid_mk_single

theorem toDirectSum_tensorPower_tprod {n} (x : Fin n → M) :
    toDirectSum (tprod R M n x) = DirectSum.of _ n (PiTensorProduct.tprod R x) :=
  by
  rw [tprod_apply, AlgHom.map_list_prod, List.map_ofFn, Function.comp]
  simp_rw [to_direct_sum_ι]
  dsimp only
  rw [DirectSum.list_prod_ofFn_of_eq_dProd]
  apply DirectSum.of_eq_of_gradedMonoid_eq
  rw [GradedMonoid.mk_list_dProd]
  rw [TensorPower.list_prod_gradedMonoid_mk_single]
#align tensor_algebra.to_direct_sum_tensor_power_tprod TensorAlgebra.toDirectSum_tensorPower_tprod

theorem toDirectSum_comp_ofDirectSum :
    toDirectSum.comp ofDirectSum = AlgHom.id R (⨁ n, (⨂[R]^n) M) :=
  by
  ext
  simp [DirectSum.lof_eq_of, -tprod_apply, to_direct_sum_tensor_power_tprod]
#align tensor_algebra.to_direct_sum_comp_of_direct_sum TensorAlgebra.toDirectSum_comp_ofDirectSum

@[simp]
theorem toDirectSum_ofDirectSum (x : ⨁ n, (⨂[R]^n) M) : (ofDirectSum x).toDirectSum = x :=
  AlgHom.congr_fun toDirectSum_comp_ofDirectSum x
#align tensor_algebra.to_direct_sum_of_direct_sum TensorAlgebra.toDirectSum_ofDirectSum

/-- The tensor algebra is isomorphic to a direct sum of tensor powers. -/
@[simps]
def equivDirectSum : TensorAlgebra R M ≃ₐ[R] ⨁ n, (⨂[R]^n) M :=
  AlgEquiv.ofAlgHom toDirectSum ofDirectSum toDirectSum_comp_ofDirectSum
    ofDirectSum_comp_toDirectSum
#align tensor_algebra.equiv_direct_sum TensorAlgebra.equivDirectSum

end TensorAlgebra


import .affnK
import linear_algebra.std_basis
import .affnKcoord_definitions

/-
    ********************
    *** Vector space ***
    ********************
-/
universes u

section implicitK

variables
{K : Type u} [field K] [inhabited K]
{dim : nat} {id_vec : fin dim → nat }{f : fm K dim id_vec} (s : spc K f)
{dim2 : nat } {id_vec2 : fin dim2 → nat} {f2 : fm K dim2 id_vec2} (s2 : spc K f2)

variables v1 v2 : vectr s
/-
Standard vector space operations
-/
@[simp]
def add_vectr_vectr (v1 v2 : vectr s) : vectr s :=  mk_vectr' s (v1.coords + v2.coords)
@[simp]
def smul_vectr (k : K) (v : vectr s) : vectr s := mk_vectr' s (k • v.coords)
@[simp]
def neg_vectr (v : vectr s) : vectr s := mk_vectr' s ((-1 : K) • v.coords)
@[simp]
def sub_vectr_vectr (v1 v2 : vectr s) : vectr s := add_vectr_vectr s v1 (neg_vectr s v2)

-- See unframed file for template for proving module
/-
Begin proofs below. Set of lemmas and type class instances culminating in a
proof that vectr s and point s form an affine space.
-/
instance has_add_vectr : has_add (vectr s) := ⟨add_vectr_vectr s⟩
--addition of vectrs is associative
lemma add_assoc_vectr : ∀ a b c : vectr s, a + b + c = a + (b + c) :=
begin
    intros,
    ext,
    have p1 : (a + b + c).coords = a.coords + b.coords + c.coords := rfl,
    have p2 : (a + (b + c)).coords = a.coords + (b.coords + c.coords) := rfl,
    rw [p1,p2],
    dsimp only [has_add.add],
    dsimp only [add_vec_vec],
    simp only [add_assoc]
end

--vectrs form an additive semigroup
instance add_semigroup_vectr : add_semigroup (vectr s) := ⟨ add_vectr_vectr s, add_assoc_vectr s⟩

#check list.eq_repeat

--definition of zero vectr
@[simp]
def vectr_zero := mk_vectr s ⟨list.repeat 0 dim, begin
        simp  *
    end⟩--@mk_vectr K _ _ n f s (0:K)
instance has_zero_vectr : has_zero (vectr s) := ⟨vectr_zero s⟩

--zero vectr plus any particular vectr is the particular vectr unchanged
lemma zero_add_vectr : ∀ a : vectr s, 0 + a = a :=
begin
    intros, ext,
    dsimp only [has_add.add],
    dsimp only [add_vectr_vectr],
    simp only [mk_vectr'],
    dsimp only [has_add.add],
    dsimp only [add_vec_vec],
    dsimp only [has_zero.zero],
    dsimp only [vectr_zero],
    simp only [mk_vectr, mk_vec_n, vector.nth, mk_vec, list.nth_le_repeat, zero_add],
end

/-
any vectr plus the 0 vectr is the original vectr
-/
lemma add_zero_vectr : ∀ a : vectr s, a + 0 = a :=
begin
    intros, ext,
    dsimp only [has_add.add],
    dsimp only [add_vectr_vectr],
    simp only [mk_vectr'],
    dsimp only [has_add.add],
    dsimp only [add_vec_vec],
    dsimp only [has_zero.zero],
    dsimp only [vectr_zero],
    simp only [mk_vectr, mk_vec_n, vector.nth, mk_vec, list.nth_le_repeat, add_zero],
end

/-
helper function for updated lean type classes
-/
@[simp]
def nsmul_vectr : ℕ → (vectr s) → (vectr s)
| nat.zero v := vectr_zero s
--| 1 v := v
| (nat.succ n) v := (add_vectr_vectr _) v (nsmul_vectr n v)

/-
vectrs form an additive monoid
-/
instance add_monoid_vectr : add_monoid (vectr s) := ⟨
    -- add_semigroup
    add_vectr_vectr s,
    add_assoc_vectr s,
    -- has_zero
    vectr_zero s,
    -- new structure
    zero_add_vectr s,
    add_zero_vectr s,
    nsmul_vectr s
⟩

/-
negation and subtraction instances
-/
instance has_neg_vectr : has_neg (vectr s) := ⟨ neg_vectr s ⟩
instance has_sub_vectr : has_sub (vectr s) := ⟨ sub_vectr_vectr s ⟩

/-
subtracting a vectr is equivalent to adding the inverse
-/
lemma sub_eq_add_neg_vectr : ∀ a b : vectr s, a - b = a + -b :=
begin
    intros,ext,
    refl
end

/-
@[protect_proj, ancestor add_monoid has_neg has_sub]
class sub_neg_monoid (G : Type u) extends add_monoid G, has_neg G, has_sub G :=
(sub := λ a b, a + -b)
(sub_eq_add_neg : ∀ a b : G, a - b = a + -b . try_refl_tac)
(gsmul : ℤ → G → G := gsmul_rec)
(gsmul_zero' : ∀ (a : G), gsmul 0 a = 0 . try_refl_tac)
(gsmul_succ' :
  ∀ (n : ℕ) (a : G), gsmul (int.of_nat n.succ) a = a + gsmul (int.of_nat n) a  . try_refl_tac)
(gsmul_neg' :
  ∀ (n : ℕ) (a : G), gsmul (-[1+ n]) a = - (gsmul n.succ a) . try_refl_tac)

-/
instance sub_neg_monoid_vectr : sub_neg_monoid (vectr s) :=
{
    neg := neg_vectr s,
    ..(show add_monoid (vectr s), by apply_instance)
}

/-
adding a vectr with its inverse yields the 0 vectr
-/
lemma add_left_neg_vectr : ∀ a : vectr s, -a + a = 0 :=
begin
    intros,
    ext,
    dsimp only [has_add.add],
    dsimp only [add_vectr_vectr],
    simp only [mk_vectr'],
    dsimp only [has_add.add],
    dsimp only [add_vec_vec],
    dsimp only [has_zero.zero],
    dsimp only [vectr_zero],
    simp only [mk_vectr, mk_vec_n, vector.nth, mk_vec, list.nth_le_repeat],
    dsimp only [has_neg.neg],
    dsimp only [neg_vectr],
    dsimp only [has_scalar.smul],
    simp only [neg_mul_eq_neg_mul_symm, one_mul, mk_vectr', add_left_neg, smul_vec],
end

/-
vectrs form an additive group
-/
instance : add_group (vectr s) := {
    add_left_neg := begin
        exact add_left_neg_vectr s,
    end,
..(show sub_neg_monoid (vectr s), by apply_instance),

}

/-
vectr addition is commutative
-/
lemma add_comm_vectr : ∀ a b : vectr s, a + b = b + a :=
begin
    intros,
    ext,
    repeat {
    have p1 : (a + b).coords = a.coords + b.coords:= rfl,
    have p2 : (b + a).coords = b.coords + a.coords := rfl,
    rw [p1,p2],
    cc
    } ,
    dsimp only [has_add.add],
    dsimp only [add_vectr_vectr],
    dsimp only [mk_vectr'],
    dsimp only [has_add.add],
    dsimp only [add_vec_vec],
    simp only [add_comm]
end

/-
vectrs form an additive commutative semigroup
-/
instance add_comm_semigroup_vectr : add_comm_semigroup (vectr s) := ⟨
    -- add_semigroup
    add_vectr_vectr s,
    add_assoc_vectr s,
    add_comm_vectr s,
⟩

/-
vectrs form an additive commutative monoid
-/
instance add_comm_monoid_vectr : add_comm_monoid (vectr s) :=
{
    add_comm := begin
        exact add_comm_vectr s
    end,
    ..(show add_monoid (vectr s), by apply_instance)
}

instance has_scalar_vectr : has_scalar K (vectr s) := ⟨
smul_vectr s,
⟩

/-
the unit scalar times a vectr is the same vectr
-/
lemma one_smul_vectr : ∀ b : vectr s, (1 : K) • b = b := begin
    intros,ext,
    repeat {
        have h0 : ((1:K) • b).coords = ((1:K)•(b.coords)) := rfl,
        rw [h0],
        simp *,
    }
end

/-
scalars multiplication associative with vectr multiplication
-/
lemma mul_smul_vectr : ∀ (x y : K) (b : vectr s), (x * y) • b = x • y • b :=
begin
    intros,
    cases b,
    ext,
    exact mul_assoc x y _,
end

/-
@[protect_proj, to_additive]
class mul_action (α : Type*) (β : Type*) [monoid α] extends has_scalar α β :=
(one_smul : ∀ b : β, (1 : α) • b = b)
(mul_smul : ∀ (x y : α) (b : β), (x * y) • b = x • y • b)
-/
instance mul_action_vectr : mul_action K (vectr s) := ⟨
one_smul_vectr s,
mul_smul_vectr s,
⟩

/-
scalars are distributive over vectrs
-/
lemma smul_add_vectr : ∀(r : K) (x y : vectr s), r • (x + y) = r • x + r • y := begin
    intros, ext,
    repeat {
    have h0 : (r • (x + y)).coords = (r • (x.coords + y.coords)) := rfl,
    have h1 : (r•x + r•y).coords = (r•x.coords + r•y.coords) := rfl,
    rw [h0,h1],
    simp *,
    }

end

/-
any scalar times the 0 vectr is the 0 vectr
-/
lemma smul_zero_vectr : ∀(r : K), r • (0 : vectr s) = 0 := begin
    intros,
    ext,
    dsimp only [has_scalar.smul],
    dsimp only [smul_vectr],
    dsimp only [has_scalar.smul],
    dsimp only [mk_vectr'],
    dsimp only [smul_vec],
    dsimp only [has_zero.zero],
    simp only [vectr_zero, mk_vectr, mk_vec_n, mk_vec, vector.nth, list.nth_le_repeat, mul_zero],
end

/-
class distrib_mul_action (M : Type*) (A : Type*) [monoid M] [add_monoid A]
  extends mul_action M A :=
(smul_add : ∀(r : M) (x y : A), r • (x + y) = r • x + r • y)
(smul_zero : ∀(r : M), r • (0 : A) = 0)
-/
instance distrib_mul_action_K_vectrK : distrib_mul_action K (vectr s) := ⟨
smul_add_vectr s,
smul_zero_vectr s,
⟩

-- vectrs are distributive over scalars
lemma add_smul_vectr : ∀ (a b : K) (x : vectr s), (a + b) • x = a • x + b • x :=
begin
  intros,
  ext,
  exact right_distrib _ _ _,
end

--0 scalar times any vectr is 0 vectr
lemma zero_smul_vectr : ∀ (x : vectr s), (0 : K) • x = 0 := begin
    intros,
    ext,
    dsimp only [has_scalar.smul],
    dsimp only [smul_vectr],
    dsimp only [has_scalar.smul],
    dsimp only [mk_vectr'],
    dsimp only [smul_vec],
    have h₁ : ((0 : vectr s).coords x_1).coord = 0 := begin
        dsimp only [has_zero.zero],
        simp only [vectr_zero, mk_vectr, mk_vec_n, mk_vec, vector.nth, list.nth_le_repeat, mul_zero],
        refl
    end,
    rw h₁,
    simp only [zero_mul]
end

--vectors form an additive commutative group
instance add_comm_group_vectr : add_comm_group (vectr s) :=
{
    add_comm := begin
        exact add_comm_vectr s

    end,
..(show add_group (vectr s), by apply_instance)
}

--Result of file is a proof that K and vectr s are a vector space (module technicaly, but vector space effectively)

instance module_K_vectrK : module K (vectr s) := ⟨
    add_smul_vectr s,
    zero_smul_vectr s,
⟩

end implicitK


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_67
  (f g : ℝ → ℝ)
  (h₀ : ∀ x, f x = 5 * x + 3)
  (h₁ : ∀ x, g x = x^2 - 2) :
  g (f (-1)) = 2 :=
begin
  norm_num [h₀, h₁],
end


import tactic --hide

/-Lemma
Let $P,Q,R$ be logical statements with $P$ implies $Q$ and $Q$ implies $R$. If $P$ is true, then so
is $R$.
-/
lemma implications_two (P Q R : Prop) (h1 : P → Q) (h2 : Q → R) (p : P) : R :=
begin
  exact h2 (h1 (p)),

end


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

/-!
# Filters used in box-based integrals

First we define a structure `box_integral.integration_params`. This structure will be used as an
argument in the definition of `box_integral.integral` in order to use the same definition for a few
well-known definitions of integrals based on partitions of a rectangular box into subboxes (Riemann
integral, Henstock-Kurzweil integral, and McShane integral).

This structure holds three boolean values (see below), and encodes eight different sets of
parameters; only four of these values are used somewhere in `mathlib`. Three of them correspond to
the integration theories listed above, and one is a generalization of the one-dimensional
Henstock-Kurzweil integral such that the divergence theorem works without additional integrability
assumptions.

Finally, for each set of parameters `l : box_integral.integration_params` and a rectangular box
`I : box_integral.box ι`, we define several `filter`s that will be used either in the definition of
the corresponding integral, or in the proofs of its properties. We equip
`box_integral.integration_params` with a `bounded_order` structure such that larger
`integration_params` produce larger filters.

## Main definitions

### Integration parameters

The structure `box_integral.integration_params` has 3 boolean fields with the following meaning:

* `bRiemann`: the value `tt` means that the filter corresponds to a Riemann-style integral, i.e. in
  the definition of integrability we require a constant upper estimate `r` on the size of boxes of a
  tagged partition; the value `ff` means that the estimate may depend on the position of the tag.

* `bHenstock`: the value `tt` means that we require that each tag belongs to its own closed box; the
  value `ff` means that we only require that tags belong to the ambient box.

* `bDistortion`: the value `tt` means that `r` can depend on the maximal ratio of sides of the same
  box of a partition. Presence of this case make quite a few proofs harder but we can prove the
  divergence theorem only for the filter
  `box_integral.integration_params.GP = ⊥ = {bRiemann := ff, bHenstock := tt, bDistortion := tt}`.

### Well-known sets of parameters

Out of eight possible values of `box_integral.integration_params`, the following four are used in
the library.

* `box_integral.integration_params.Riemann` (`bRiemann = tt`, `bHenstock = tt`, `bDistortion = ff`):
  this value corresponds to the Riemann integral; in the corresponding filter, we require that the
  diameters of all boxes `J` of a tagged partition are bounded from above by a constant upper
  estimate that may not depend on the geometry of `J`, and each tag belongs to the corresponding
  closed box.

* `box_integral.integration_params.Henstock` (`bRiemann = ff`, `bHenstock = tt`,
  `bDistortion = ff`): this value corresponds to the most natural generalization of
  Henstock-Kurzweil integral to higher dimension; the only (but important!) difference between this
  theory and Riemann integral is that instead of a constant upper estimate on the size of all boxes
  of a partition, we require that the partition is *subordinate* to a possibly discontinuous
  function `r : (ι → ℝ) → {x : ℝ | 0 < x}`, i.e. each box `J` is included in a closed ball with
  center `π.tag J` and radius `r J`.

* `box_integral.integration_params.McShane` (`bRiemann = ff`, `bHenstock = ff`, `bDistortion = ff`):
  this value corresponds to the McShane integral; the only difference with the Henstock integral is
  that we allow tags to be outside of their boxes; the tags still have to be in the ambient closed
  box, and the partition still has to be subordinate to a function.

* `box_integral.integration_params.GP = ⊥` (`bRiemann = ff`, `bHenstock = tt`, `bDistortion = tt`):
  this is the least integration theory in our list, i.e., all functions integrable in any other
  theory is integrable in this one as well.  This is a non-standard generalization of the
  Henstock-Kurzweil integral to higher dimension.  In dimension one, it generates the same filter as
  `Henstock`. In higher dimension, this generalization defines an integration theory such that the
  divergence of any Fréchet differentiable function `f` is integrable, and its integral is equal to
  the sum of integrals of `f` over the faces of the box, taken with appropriate signs.

  A function `f` is `GP`-integrable if for any `ε > 0` and `c : ℝ≥0` there exists
  `r : (ι → ℝ) → {x : ℝ | 0 < x}` such that for any tagged partition `π` subordinate to `r`, if each
  tag belongs to the corresponding closed box and for each box `J ∈ π`, the maximal ratio of its
  sides is less than or equal to `c`, then the integral sum of `f` over `π` is `ε`-close to the
  integral.

### Filters and predicates on `tagged_prepartition I`

For each value of `integration_params` and a rectangular box `I`, we define a few filters on
`tagged_prepartition I`. First, we define a predicate

```
structure box_integral.integration_params.mem_base_set (l : box_integral.integration_params)
  (I : box_integral.box ι) (c : ℝ≥0) (r : (ι → ℝ) → Ioi (0 : ℝ))
  (π : box_integral.tagged_prepartition I) : Prop :=
```

This predicate says that

* if `l.bHenstock`, then `π` is a Henstock prepartition, i.e. each tag belongs to the corresponding
  closed box;
* `π` is subordinate to `r`;
* if `l.bDistortion`, then the distortion of each box in `π` is less than or equal to `c`;
* if `l.bDistortion`, then there exists a prepartition `π'` with distortion `≤ c` that covers
  exactly `I \ π.Union`.

The last condition is always true for `c > 1`, see TODO section for more details.

Then we define a predicate `box_integral.integration_params.r_cond` on functions
`r : (ι → ℝ) → {x : ℝ | 0 < x}`. If `l.bRiemann`, then this predicate requires `r` to be a constant
function, otherwise it imposes no restrictions on `r`. We introduce this definition to prove a few
dot-notation lemmas: e.g., `box_integral.integration_params.r_cond.min` says that the pointwise
minimum of two functions that satisfy this condition satisfies this condition as well.

Then we define four filters on `box_integral.tagged_prepartition I`.

* `box_integral.integration_params.to_filter_distortion`: an auxiliary filter that takes parameters
  `(l : box_integral.integration_params) (I : box_integral.box ι) (c : ℝ≥0)` and returns the
  filter generated by all sets `{π | mem_base_set l I c r π}`, where `r` is a function satisfying
  the predicate `box_integral.integration_params.r_cond l`;

* `box_integral.integration_params.to_filter l I`: the supremum of `l.to_filter_distortion I c`
  over all `c : ℝ≥0`;

* `box_integral.integration_params.to_filter_distortion_Union l I c π₀`, where `π₀` is a
  prepartition of `I`: the infimum of `l.to_filter_distortion I c` and the principal filter
  generated by `{π | π.Union = π₀.Union}`;

* `box_integral.integration_params.to_filter_Union l I π₀`: the supremum of
  `l.to_filter_distortion_Union l I c π₀` over all `c : ℝ≥0`. This is the filter (in the case
  `π₀ = ⊤` is the one-box partition of `I`) used in the definition of the integral of a function
  over a box.

## Implementation details

* Later we define the integral of a function over a rectangular box as the limit (if it exists) of
  the integral sums along `box_integral.integration_params.to_filter_Union l I ⊤`. While it is
  possible to define the integral with a general filter on `box_integral.tagged_prepartition I` as a
  parameter, many lemmas (e.g., Sacks-Henstock lemma and most results about integrability of
  functions) require the filter to have a predictable structure. So, instead of adding assumptions
  about the filter here and there, we define this auxiliary type that can encode all integration
  theories we need in practice.

* While the definition of the integral only uses the filter
  `box_integral.integration_params.to_filter_Union l I ⊤` and partitions of a box, some lemmas
  (e.g., the Henstock-Sacks lemmas) are best formulated in terms of the predicate `mem_base_set` and
  other filters defined above.

* We use `bool` instead of `Prop` for the fields of `integration_params` in order to have decidable
  equality and inequalities.

## TODO

Currently, `box_integral.integration_params.mem_base_set` explicitly requires that there exists a
partition of the complement `I \ π.Union` with distortion `≤ c`. For `c > 1`, this condition is
always true but the proof of this fact requires more API about
`box_integral.prepartition.split_many`. We should formalize this fact, then either require `c > 1`
everywhere, or replace `≤ c` with `< c` so that we automatically get `c > 1` for a non-trivial
prepartition (and consider the special case `π = ⊥` separately if needed).

## Tags

integral, rectangular box, partition, filter
-/

open set function filter metric finset bool
open_locale classical topology filter nnreal
noncomputable theory

namespace box_integral

variables {ι : Type*} [fintype ι] {I J : box ι} {c c₁ c₂ : ℝ≥0} {r r₁ r₂ : (ι → ℝ) → Ioi (0 : ℝ)}
  {π π₁ π₂ : tagged_prepartition I}

open tagged_prepartition

/-- An `integration_params` is a structure holding 3 boolean values used to define a filter to be
used in the definition of a box-integrable function.

* `bRiemann`: the value `tt` means that the filter corresponds to a Riemann-style integral, i.e. in
  the definition of integrability we require a constant upper estimate `r` on the size of boxes of a
  tagged partition; the value `ff` means that the estimate may depend on the position of the tag.

* `bHenstock`: the value `tt` means that we require that each tag belongs to its own closed box; the
  value `ff` means that we only require that tags belong to the ambient box.

* `bDistortion`: the value `tt` means that `r` can depend on the maximal ratio of sides of the same
  box of a partition. Presence of this case makes quite a few proofs harder but we can prove the
  divergence theorem only for the filter
  `box_integral.integration_params.GP = ⊥ = {bRiemann := ff, bHenstock := tt, bDistortion := tt}`.
-/
@[ext] structure integration_params : Type :=
(bRiemann bHenstock bDistortion : bool)

variables {l l₁ l₂ : integration_params}

namespace integration_params

/-- Auxiliary equivalence with a product type used to lift an order. -/
def equiv_prod : integration_params ≃ bool × boolᵒᵈ × boolᵒᵈ :=
{ to_fun := λ l, ⟨l.1, order_dual.to_dual l.2, order_dual.to_dual l.3⟩,
  inv_fun := λ l, ⟨l.1, order_dual.of_dual l.2.1, order_dual.of_dual l.2.2⟩,
  left_inv := λ ⟨a, b, c⟩, rfl,
  right_inv := λ ⟨a, b, c⟩, rfl }

instance : partial_order integration_params :=
partial_order.lift equiv_prod equiv_prod.injective

/-- Auxiliary `order_iso` with a product type used to lift a `bounded_order` structure. -/
def iso_prod : integration_params ≃o bool × boolᵒᵈ × boolᵒᵈ :=
⟨equiv_prod, λ ⟨x, y, z⟩, iff.rfl⟩

instance : bounded_order integration_params :=
iso_prod.symm.to_galois_insertion.lift_bounded_order

/-- The value
`box_integral.integration_params.GP = ⊥` (`bRiemann = ff`, `bHenstock = tt`, `bDistortion = tt`)
corresponds to a generalization of the Henstock integral such that the Divergence theorem holds true
without additional integrability assumptions, see the module docstring for details. -/
instance : inhabited integration_params := ⟨⊥⟩

instance : decidable_rel ((≤) : integration_params → integration_params → Prop) :=
λ _ _, and.decidable

instance : decidable_eq integration_params := λ x y, decidable_of_iff _ (ext_iff x y).symm

/-- The `box_integral.integration_params` corresponding to the Riemann integral. In the
corresponding filter, we require that the diameters of all boxes `J` of a tagged partition are
bounded from above by a constant upper estimate that may not depend on the geometry of `J`, and each
tag belongs to the corresponding closed box. -/
def Riemann : integration_params :=
{ bRiemann := tt,
  bHenstock := tt,
  bDistortion := ff }

/-- The `box_integral.integration_params` corresponding to the Henstock-Kurzweil integral. In the
corresponding filter, we require that the tagged partition is subordinate to a (possibly,
discontinuous) positive function `r` and each tag belongs to the corresponding closed box. -/
def Henstock : integration_params := ⟨ff, tt, ff⟩

/-- The `box_integral.integration_params` corresponding to the McShane integral. In the
corresponding filter, we require that the tagged partition is subordinate to a (possibly,
discontinuous) positive function `r`; the tags may be outside of the corresponding closed box
(but still inside the ambient closed box `I.Icc`). -/
def McShane : integration_params := ⟨ff, ff, ff⟩

/-- The `box_integral.integration_params` corresponding to the generalized Perron integral. In the
corresponding filter, we require that the tagged partition is subordinate to a (possibly,
discontinuous) positive function `r` and each tag belongs to the corresponding closed box. We also
require an upper estimate on the distortion of all boxes of the partition. -/
def GP : integration_params := ⊥

lemma Henstock_le_Riemann : Henstock ≤ Riemann := dec_trivial

lemma Henstock_le_McShane : Henstock ≤ McShane := dec_trivial

lemma GP_le : GP ≤ l := bot_le

/-- The predicate corresponding to a base set of the filter defined by an
`integration_params`. It says that

* if `l.bHenstock`, then `π` is a Henstock prepartition, i.e. each tag belongs to the corresponding
  closed box;
* `π` is subordinate to `r`;
* if `l.bDistortion`, then the distortion of each box in `π` is less than or equal to `c`;
* if `l.bDistortion`, then there exists a prepartition `π'` with distortion `≤ c` that covers
  exactly `I \ π.Union`.

The last condition is automatically verified for partitions, and is used in the proof of the
Sacks-Henstock inequality to compare two prepartitions covering the same part of the box.

It is also automatically satisfied for any `c > 1`, see TODO section of the module docstring for
details. -/
@[protect_proj] structure mem_base_set (l : integration_params) (I : box ι) (c : ℝ≥0)
  (r : (ι → ℝ) → Ioi (0 : ℝ)) (π : tagged_prepartition I) : Prop :=
(is_subordinate : π.is_subordinate r)
(is_Henstock : l.bHenstock → π.is_Henstock)
(distortion_le : l.bDistortion → π.distortion ≤ c)
(exists_compl : l.bDistortion → ∃ π' : prepartition I, π'.Union = I \ π.Union ∧ π'.distortion ≤ c)

/-- A predicate saying that in case `l.bRiemann = tt`, the function `r` is a constant. -/
def r_cond {ι : Type*} (l : integration_params) (r : (ι → ℝ) → Ioi (0 : ℝ)) : Prop :=
l.bRiemann → ∀ x, r x = r 0

/-- A set `s : set (tagged_prepartition I)` belongs to `l.to_filter_distortion I c` if there exists
a function `r : ℝⁿ → (0, ∞)` (or a constant `r` if `l.bRiemann = tt`) such that `s` contains each
prepartition `π` such that `l.mem_base_set I c r π`. -/
def to_filter_distortion (l : integration_params) (I : box ι) (c : ℝ≥0) :
  filter (tagged_prepartition I) :=
⨅ (r : (ι → ℝ) → Ioi (0 : ℝ)) (hr : l.r_cond r), 𝓟 {π | l.mem_base_set I c r π}

/-- A set `s : set (tagged_prepartition I)` belongs to `l.to_filter I` if for any `c : ℝ≥0` there
exists a function `r : ℝⁿ → (0, ∞)` (or a constant `r` if `l.bRiemann = tt`) such that
`s` contains each prepartition `π` such that `l.mem_base_set I c r π`. -/
def to_filter (l : integration_params) (I : box ι) :
  filter (tagged_prepartition I) :=
⨆ c : ℝ≥0, l.to_filter_distortion I c

/-- A set `s : set (tagged_prepartition I)` belongs to `l.to_filter_distortion_Union I c π₀` if
there exists a function `r : ℝⁿ → (0, ∞)` (or a constant `r` if `l.bRiemann = tt`) such that `s`
contains each prepartition `π` such that `l.mem_base_set I c r π` and `π.Union = π₀.Union`. -/
def to_filter_distortion_Union (l : integration_params) (I : box ι) (c : ℝ≥0)
  (π₀ : prepartition I) :=
l.to_filter_distortion I c ⊓ 𝓟 {π | π.Union = π₀.Union}

/-- A set `s : set (tagged_prepartition I)` belongs to `l.to_filter_Union I π₀` if for any `c : ℝ≥0`
there exists a function `r : ℝⁿ → (0, ∞)` (or a constant `r` if `l.bRiemann = tt`) such that `s`
contains each prepartition `π` such that `l.mem_base_set I c r π` and `π.Union = π₀.Union`. -/
def to_filter_Union (l : integration_params) (I : box ι) (π₀ : prepartition I) :=
⨆ c : ℝ≥0, l.to_filter_distortion_Union I c π₀

lemma r_cond_of_bRiemann_eq_ff {ι} (l : integration_params) (hl : l.bRiemann = ff)
  {r : (ι → ℝ) → Ioi (0 : ℝ)} :
  l.r_cond r :=
by simp [r_cond, hl]

lemma to_filter_inf_Union_eq (l : integration_params) (I : box ι) (π₀ : prepartition I) :
  l.to_filter I ⊓ 𝓟 {π | π.Union = π₀.Union} = l.to_filter_Union I π₀ :=
(supr_inf_principal _ _).symm

lemma mem_base_set.mono' (I : box ι) (h : l₁ ≤ l₂) (hc : c₁ ≤ c₂) {π : tagged_prepartition I}
  (hr : ∀ J ∈ π, r₁ (π.tag J) ≤ r₂ (π.tag J)) (hπ : l₁.mem_base_set I c₁ r₁ π) :
  l₂.mem_base_set I c₂ r₂ π :=
⟨hπ.1.mono' hr, λ h₂, hπ.2 (le_iff_imp.1 h.2.1 h₂),
  λ hD, (hπ.3 (le_iff_imp.1 h.2.2 hD)).trans hc,
  λ hD, (hπ.4 (le_iff_imp.1 h.2.2 hD)).imp $ λ π hπ, ⟨hπ.1, hπ.2.trans hc⟩⟩

@[mono] lemma mem_base_set.mono (I : box ι) (h : l₁ ≤ l₂) (hc : c₁ ≤ c₂) {π : tagged_prepartition I}
  (hr : ∀ x ∈ I.Icc, r₁ x ≤ r₂ x) (hπ : l₁.mem_base_set I c₁ r₁ π) :
  l₂.mem_base_set I c₂ r₂ π :=
hπ.mono' I h hc $ λ J hJ, hr _ $ π.tag_mem_Icc J

lemma mem_base_set.exists_common_compl (h₁ : l.mem_base_set I c₁ r₁ π₁)
  (h₂ : l.mem_base_set I c₂ r₂ π₂) (hU : π₁.Union = π₂.Union) :
  ∃ π : prepartition I, π.Union = I \ π₁.Union ∧
    (l.bDistortion → π.distortion ≤ c₁) ∧ (l.bDistortion → π.distortion ≤ c₂) :=
begin
  wlog hc : c₁ ≤ c₂,
  { simpa [hU, and_comm] using this h₂ h₁ hU.symm (le_of_not_le hc) },
  by_cases hD : (l.bDistortion : Prop),
  { rcases h₁.4 hD with ⟨π, hπU, hπc⟩,
    exact ⟨π, hπU, λ _, hπc, λ _, hπc.trans hc⟩ },
  { exact ⟨π₁.to_prepartition.compl, π₁.to_prepartition.Union_compl,
      λ h, (hD h).elim, λ h, (hD h).elim⟩ }
end

protected lemma mem_base_set.union_compl_to_subordinate (hπ₁ : l.mem_base_set I c r₁ π₁)
  (hle : ∀ x ∈ I.Icc, r₂ x ≤ r₁ x) {π₂ : prepartition I}
  (hU : π₂.Union = I \ π₁.Union) (hc : l.bDistortion → π₂.distortion ≤ c) :
  l.mem_base_set I c r₁ (π₁.union_compl_to_subordinate π₂ hU r₂) :=
⟨hπ₁.1.disj_union ((π₂.is_subordinate_to_subordinate r₂).mono hle) _,
  λ h, ((hπ₁.2 h).disj_union (π₂.is_Henstock_to_subordinate _) _),
  λ h, (distortion_union_compl_to_subordinate _ _ _ _).trans_le (max_le (hπ₁.3 h) (hc h)),
  λ _, ⟨⊥, by simp⟩⟩

protected lemma mem_base_set.filter (hπ : l.mem_base_set I c r π) (p : box ι → Prop) :
  l.mem_base_set I c r (π.filter p) :=
begin
  refine ⟨λ J hJ, hπ.1 J (π.mem_filter.1 hJ).1, λ hH J hJ, hπ.2 hH J (π.mem_filter.1 hJ).1,
    λ hD, (distortion_filter_le _ _).trans (hπ.3 hD), λ hD, _⟩,
  rcases hπ.4 hD with ⟨π₁, hπ₁U, hc⟩,
  set π₂ := π.filter (λ J, ¬p J),
  have : disjoint π₁.Union π₂.Union,
    by simpa [π₂, hπ₁U] using disjoint_sdiff_self_left.mono_right sdiff_le,
  refine ⟨π₁.disj_union π₂.to_prepartition this, _, _⟩,
  { suffices : ↑I \ π.Union ∪ π.Union \ (π.filter p).Union = ↑I \ (π.filter p).Union, by simpa *,
    have : (π.filter p).Union ⊆ π.Union, from bUnion_subset_bUnion_left (finset.filter_subset _ _),
    ext x, fsplit,
    { rintro (⟨hxI, hxπ⟩|⟨hxπ, hxp⟩),
      exacts [⟨hxI, mt (@this x) hxπ⟩, ⟨π.Union_subset hxπ, hxp⟩] },
    { rintro ⟨hxI, hxp⟩, by_cases hxπ : x ∈ π.Union,
      exacts [or.inr ⟨hxπ, hxp⟩, or.inl ⟨hxI, hxπ⟩] } },
  { have : (π.filter (λ J, ¬p J)).distortion ≤ c, from (distortion_filter_le _ _).trans (hπ.3 hD),
    simpa [hc] }
end

lemma bUnion_tagged_mem_base_set {π : prepartition I} {πi : Π J, tagged_prepartition J}
  (h : ∀ J ∈ π, l.mem_base_set J c r (πi J)) (hp : ∀ J ∈ π, (πi J).is_partition)
  (hc : l.bDistortion → π.compl.distortion ≤ c) :
  l.mem_base_set I c r (π.bUnion_tagged πi) :=
begin
  refine ⟨tagged_prepartition.is_subordinate_bUnion_tagged.2 $ λ J hJ, (h J hJ).1,
    λ hH, tagged_prepartition.is_Henstock_bUnion_tagged.2 $ λ J hJ, (h J hJ).2 hH,
    λ hD, _, λ hD, _⟩,
  { rw [prepartition.distortion_bUnion_tagged, finset.sup_le_iff],
    exact λ J hJ, (h J hJ).3 hD },
  { refine ⟨_, _, hc hD⟩,
    rw [π.Union_compl, ← π.Union_bUnion_partition hp], refl }
end

@[mono] lemma r_cond.mono {ι : Type*} {r : (ι → ℝ) → Ioi (0 : ℝ)} (h : l₁ ≤ l₂)
  (hr :  l₂.r_cond r) : l₁.r_cond r :=
λ hR, hr (le_iff_imp.1 h.1 hR)

@[mono] lemma to_filter_distortion_mono (I : box ι) (h : l₁ ≤ l₂) (hc : c₁ ≤ c₂) :
  l₁.to_filter_distortion I c₁ ≤ l₂.to_filter_distortion I c₂ :=
infi_mono $ λ r, infi_mono' $ λ hr,
  ⟨hr.mono h, principal_mono.2 $ λ _, mem_base_set.mono I h hc (λ _ _, le_rfl)⟩

@[mono] lemma to_filter_mono (I : box ι) {l₁ l₂ : integration_params} (h : l₁ ≤ l₂) :
  l₁.to_filter I ≤ l₂.to_filter I :=
supr_mono $ λ c, to_filter_distortion_mono I h le_rfl

@[mono] lemma to_filter_Union_mono (I : box ι) {l₁ l₂ : integration_params} (h : l₁ ≤ l₂)
  (π₀ : prepartition I) :
  l₁.to_filter_Union I π₀ ≤ l₂.to_filter_Union I π₀ :=
supr_mono $ λ c, inf_le_inf_right _ $ to_filter_distortion_mono _ h le_rfl

lemma to_filter_Union_congr (I : box ι) (l : integration_params) {π₁ π₂ : prepartition I}
  (h : π₁.Union = π₂.Union) : l.to_filter_Union I π₁ = l.to_filter_Union I π₂ :=
by simp only [to_filter_Union, to_filter_distortion_Union, h]

lemma has_basis_to_filter_distortion (l : integration_params) (I : box ι) (c : ℝ≥0) :
  (l.to_filter_distortion I c).has_basis l.r_cond (λ r, {π | l.mem_base_set I c r π}) :=
has_basis_binfi_principal'
  (λ r₁ hr₁ r₂ hr₂, ⟨_, hr₁.min hr₂,
    λ _, mem_base_set.mono _ le_rfl le_rfl (λ x hx, min_le_left _ _),
    λ _, mem_base_set.mono _ le_rfl le_rfl (λ x hx, min_le_right _ _)⟩)
  ⟨λ _, ⟨1, zero_lt_one⟩, λ _ _, rfl⟩

lemma has_basis_to_filter_distortion_Union (l : integration_params) (I : box ι) (c : ℝ≥0)
  (π₀ : prepartition I) :
  (l.to_filter_distortion_Union I c π₀).has_basis l.r_cond
    (λ r, {π | l.mem_base_set I c r π ∧ π.Union = π₀.Union}) :=
(l.has_basis_to_filter_distortion I c).inf_principal _

lemma has_basis_to_filter_Union (l : integration_params) (I : box ι) (π₀ : prepartition I) :
  (l.to_filter_Union I π₀).has_basis (λ r : ℝ≥0 → (ι → ℝ) → Ioi (0 : ℝ), ∀ c, l.r_cond (r c))
    (λ r, {π | ∃ c, l.mem_base_set I c (r c) π ∧ π.Union = π₀.Union}) :=
have _ := λ c, l.has_basis_to_filter_distortion_Union I c π₀,
by simpa only [set_of_and, set_of_exists] using has_basis_supr this

lemma has_basis_to_filter_Union_top (l : integration_params) (I : box ι) :
  (l.to_filter_Union I ⊤).has_basis (λ r : ℝ≥0 → (ι → ℝ) → Ioi (0 : ℝ), ∀ c, l.r_cond (r c))
    (λ r, {π | ∃ c, l.mem_base_set I c (r c) π ∧ π.is_partition}) :=
by simpa only [tagged_prepartition.is_partition_iff_Union_eq, prepartition.Union_top]
  using l.has_basis_to_filter_Union I ⊤

lemma has_basis_to_filter (l : integration_params) (I : box ι) :
  (l.to_filter I).has_basis (λ r : ℝ≥0 → (ι → ℝ) → Ioi (0 : ℝ), ∀ c, l.r_cond (r c))
    (λ r, {π | ∃ c, l.mem_base_set I c (r c) π}) :=
by simpa only [set_of_exists] using has_basis_supr (l.has_basis_to_filter_distortion I)

lemma tendsto_embed_box_to_filter_Union_top (l : integration_params) (h : I ≤ J) :
  tendsto (tagged_prepartition.embed_box I J h) (l.to_filter_Union I ⊤)
    (l.to_filter_Union J (prepartition.single J I h)) :=
begin
  simp only [to_filter_Union, tendsto_supr], intro c,
  set π₀ := (prepartition.single J I h),
  refine le_supr_of_le (max c π₀.compl.distortion) _,
  refine ((l.has_basis_to_filter_distortion_Union I c ⊤).tendsto_iff
    (l.has_basis_to_filter_distortion_Union J _ _)).2 (λ r hr, _),
  refine ⟨r, hr, λ π hπ, _⟩,
  rw [mem_set_of_eq, prepartition.Union_top] at hπ,
  refine ⟨⟨hπ.1.1, hπ.1.2, λ hD, le_trans (hπ.1.3 hD) (le_max_left _ _), λ hD, _⟩, _⟩,
  { refine ⟨_, π₀.Union_compl.trans _, le_max_right _ _⟩, congr' 1,
    exact (prepartition.Union_single h).trans hπ.2.symm },
  { exact hπ.2.trans (prepartition.Union_single _).symm }
end

lemma exists_mem_base_set_le_Union_eq (l : integration_params) (π₀ : prepartition I)
  (hc₁ : π₀.distortion ≤ c) (hc₂ : π₀.compl.distortion ≤ c) (r : (ι → ℝ) → Ioi (0 : ℝ)) :
  ∃ π, l.mem_base_set I c r π ∧ π.to_prepartition ≤ π₀ ∧ π.Union = π₀.Union :=
begin
  rcases π₀.exists_tagged_le_is_Henstock_is_subordinate_Union_eq r
    with ⟨π, hle, hH, hr, hd, hU⟩,
  refine ⟨π, ⟨hr, λ _, hH, λ _, hd.trans_le hc₁, λ hD, ⟨π₀.compl, _, hc₂⟩⟩, ⟨hle, hU⟩⟩,
  exact prepartition.compl_congr hU ▸ π.to_prepartition.Union_compl
end

lemma exists_mem_base_set_is_partition (l : integration_params) (I : box ι)
  (hc : I.distortion ≤ c) (r : (ι → ℝ) → Ioi (0 : ℝ)) :
  ∃ π, l.mem_base_set I c r π ∧ π.is_partition :=
begin
  rw ← prepartition.distortion_top at hc,
  have hc' : (⊤ : prepartition I).compl.distortion ≤ c, by simp,
  simpa [is_partition_iff_Union_eq] using l.exists_mem_base_set_le_Union_eq ⊤ hc hc' r
end

lemma to_filter_distortion_Union_ne_bot (l : integration_params) (I : box ι)
  (π₀ : prepartition I) (hc₁ : π₀.distortion ≤ c) (hc₂ : π₀.compl.distortion ≤ c) :
  (l.to_filter_distortion_Union I c π₀).ne_bot :=
((l.has_basis_to_filter_distortion I _).inf_principal _).ne_bot_iff.2 $ λ r hr,
  (l.exists_mem_base_set_le_Union_eq π₀ hc₁ hc₂ r).imp $ λ π hπ, ⟨hπ.1, hπ.2.2⟩

instance to_filter_distortion_Union_ne_bot' (l : integration_params) (I : box ι)
  (π₀ : prepartition I) :
  (l.to_filter_distortion_Union I (max π₀.distortion π₀.compl.distortion) π₀).ne_bot :=
l.to_filter_distortion_Union_ne_bot I π₀ (le_max_left _ _) (le_max_right _ _)

instance to_filter_distortion_ne_bot (l : integration_params) (I : box ι) :
  (l.to_filter_distortion I I.distortion).ne_bot :=
by simpa using (l.to_filter_distortion_Union_ne_bot' I ⊤).mono inf_le_left

instance to_filter_ne_bot (l : integration_params) (I : box ι) : (l.to_filter I).ne_bot :=
(l.to_filter_distortion_ne_bot I).mono $ le_supr _ _

instance to_filter_Union_ne_bot (l : integration_params) (I : box ι) (π₀ : prepartition I) :
  (l.to_filter_Union I π₀).ne_bot :=
(l.to_filter_distortion_Union_ne_bot' I π₀).mono $
  le_supr (λ c, l.to_filter_distortion_Union I c π₀) _

lemma eventually_is_partition (l : integration_params) (I : box ι) :
  ∀ᶠ π in l.to_filter_Union I ⊤, tagged_prepartition.is_partition π :=
eventually_supr.2 $ λ c, eventually_inf_principal.2 $ eventually_of_forall $
  λ π h, π.is_partition_iff_Union_eq.2 (h.trans prepartition.Union_top)

end integration_params

end box_integral


-- Propiedad semidistributiva de la intersección sobre la unión
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 21-abril-2022
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Demostrar que
--    s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u)
-- ----------------------------------------------------------------------

import data.set.basic
import tactic

open set

variable {α : Type}
variables s t u : set α

-- 1ª demostración
-- ===============

example :
  s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u) :=
begin
  intros x hx,
  cases hx with hxs hxtu,
  cases hxtu with hxt hxu,
  { left,
    split,
    { exact hxs, },
    { exact hxt, }},
  { right,
    split,
    { exact hxs, },
    { exact hxu, }},
end

-- 2ª demostración
-- ===============

example :
  s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u) :=
begin
  rintros x ⟨hxs, hxt | hxu⟩,
  { left,
    exact ⟨hxs, hxt⟩, },
  { right,
    exact ⟨hxs, hxu⟩, },
end

-- 3ª demostración
-- ===============

example :
  s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u) :=
begin
  rintros x ⟨hxs, hxt | hxu⟩,
  { exact or.inl ⟨hxs, hxt⟩, },
  { exact or.inr ⟨hxs, hxu⟩, },
end

-- 4ª demostración
-- ===============

example :
  s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u) :=
begin
  intros x hx,
  by finish,
end

-- 5ª demostración
-- ===============

example :
  s ∩ (t ∪ u) ⊆ (s ∩ t) ∪ (s ∩ u) :=
by rw inter_union_distrib_left


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura

! This file was ported from Lean 3 source module init.cc_lemmas
! leanprover-community/mathlib commit 4a03bdeb31b3688c31d02d7ff8e0ff2e5d6174db
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
prelude
import Leanbin.Init.Propext
import Leanbin.Init.Classical

/-! Lemmas use by the congruence closure module -/

#print iff_eq_of_eq_true_left /-
theorem iff_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a ↔ b) = b :=
  h.symm ▸ propext (true_iff_iff _)
#align iff_eq_of_eq_true_left iff_eq_of_eq_true_left
-/

#print iff_eq_of_eq_true_right /-
theorem iff_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a ↔ b) = a :=
  h.symm ▸ propext (iff_true_iff _)
#align iff_eq_of_eq_true_right iff_eq_of_eq_true_right
-/

#print iff_eq_true_of_eq /-
theorem iff_eq_true_of_eq {a b : Prop} (h : a = b) : (a ↔ b) = True :=
  h ▸ propext (iff_self_iff _)
#align iff_eq_true_of_eq iff_eq_true_of_eq
-/

#print and_eq_of_eq_true_left /-
theorem and_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a ∧ b) = b :=
  h.symm ▸ propext (true_and_iff _)
#align and_eq_of_eq_true_left and_eq_of_eq_true_left
-/

#print and_eq_of_eq_true_right /-
theorem and_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a ∧ b) = a :=
  h.symm ▸ propext (and_true_iff _)
#align and_eq_of_eq_true_right and_eq_of_eq_true_right
-/

#print and_eq_of_eq_false_left /-
theorem and_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a ∧ b) = False :=
  h.symm ▸ propext (false_and_iff _)
#align and_eq_of_eq_false_left and_eq_of_eq_false_left
-/

#print and_eq_of_eq_false_right /-
theorem and_eq_of_eq_false_right {a b : Prop} (h : b = False) : (a ∧ b) = False :=
  h.symm ▸ propext (and_false_iff _)
#align and_eq_of_eq_false_right and_eq_of_eq_false_right
-/

#print and_eq_of_eq /-
theorem and_eq_of_eq {a b : Prop} (h : a = b) : (a ∧ b) = a :=
  h ▸ propext (and_self_iff _)
#align and_eq_of_eq and_eq_of_eq
-/

#print or_eq_of_eq_true_left /-
theorem or_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a ∨ b) = True :=
  h.symm ▸ propext (true_or_iff _)
#align or_eq_of_eq_true_left or_eq_of_eq_true_left
-/

#print or_eq_of_eq_true_right /-
theorem or_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a ∨ b) = True :=
  h.symm ▸ propext (or_true_iff _)
#align or_eq_of_eq_true_right or_eq_of_eq_true_right
-/

#print or_eq_of_eq_false_left /-
theorem or_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a ∨ b) = b :=
  h.symm ▸ propext (false_or_iff _)
#align or_eq_of_eq_false_left or_eq_of_eq_false_left
-/

#print or_eq_of_eq_false_right /-
theorem or_eq_of_eq_false_right {a b : Prop} (h : b = False) : (a ∨ b) = a :=
  h.symm ▸ propext (or_false_iff _)
#align or_eq_of_eq_false_right or_eq_of_eq_false_right
-/

#print or_eq_of_eq /-
theorem or_eq_of_eq {a b : Prop} (h : a = b) : (a ∨ b) = a :=
  h ▸ propext (or_self_iff _)
#align or_eq_of_eq or_eq_of_eq
-/

#print imp_eq_of_eq_true_left /-
theorem imp_eq_of_eq_true_left {a b : Prop} (h : a = True) : (a → b) = b :=
  h.symm ▸ propext (Iff.intro (fun h => h trivial) fun h₁ h₂ => h₁)
#align imp_eq_of_eq_true_left imp_eq_of_eq_true_left
-/

#print imp_eq_of_eq_true_right /-
theorem imp_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a → b) = True :=
  h.symm ▸ propext (Iff.intro (fun h => trivial) fun h₁ h₂ => h₁)
#align imp_eq_of_eq_true_right imp_eq_of_eq_true_right
-/

#print imp_eq_of_eq_false_left /-
theorem imp_eq_of_eq_false_left {a b : Prop} (h : a = False) : (a → b) = True :=
  h.symm ▸ propext (Iff.intro (fun h => trivial) fun h₁ h₂ => False.elim h₂)
#align imp_eq_of_eq_false_left imp_eq_of_eq_false_left
-/

#print imp_eq_of_eq_false_right /-
theorem imp_eq_of_eq_false_right {a b : Prop} (h : b = False) : (a → b) = Not a :=
  h.symm ▸ propext (Iff.intro (fun h => h) fun hna ha => hna ha)
#align imp_eq_of_eq_false_right imp_eq_of_eq_false_right
-/

#print not_imp_eq_of_eq_false_right /-
/-- Remark: the congruence closure module will only use this lemma if
   cc_config.em is tt. -/
theorem not_imp_eq_of_eq_false_right {a b : Prop} (h : b = False) : (Not a → b) = a :=
  h.symm ▸ propext (Iff.intro (fun h' => by_contradiction fun hna => h' hna) fun ha hna => hna ha)
#align not_imp_eq_of_eq_false_right not_imp_eq_of_eq_false_right
-/

#print imp_eq_true_of_eq /-
theorem imp_eq_true_of_eq {a b : Prop} (h : a = b) : (a → b) = True :=
  h ▸ propext (Iff.intro (fun h => trivial) fun h ha => ha)
#align imp_eq_true_of_eq imp_eq_true_of_eq
-/

#print not_eq_of_eq_true /-
theorem not_eq_of_eq_true {a : Prop} (h : a = True) : Not a = False :=
  h.symm ▸ propext not_true
#align not_eq_of_eq_true not_eq_of_eq_true
-/

#print not_eq_of_eq_false /-
theorem not_eq_of_eq_false {a : Prop} (h : a = False) : Not a = True :=
  h.symm ▸ propext not_false_iff
#align not_eq_of_eq_false not_eq_of_eq_false
-/

#print false_of_a_eq_not_a /-
theorem false_of_a_eq_not_a {a : Prop} (h : a = Not a) : False :=
  have : Not a := fun ha => absurd ha (Eq.mp h ha)
  absurd (Eq.mpr h this) this
#align false_of_a_eq_not_a false_of_a_eq_not_a
-/

universe u

#print if_eq_of_eq_true /-
theorem if_eq_of_eq_true {c : Prop} [d : Decidable c] {α : Sort u} (t e : α) (h : c = True) :
    @ite α c d t e = t :=
  if_pos (of_eq_true h)
#align if_eq_of_eq_true if_eq_of_eq_true
-/

#print if_eq_of_eq_false /-
theorem if_eq_of_eq_false {c : Prop} [d : Decidable c] {α : Sort u} (t e : α) (h : c = False) :
    @ite α c d t e = e :=
  if_neg (not_of_eq_false h)
#align if_eq_of_eq_false if_eq_of_eq_false
-/

#print if_eq_of_eq /-
theorem if_eq_of_eq (c : Prop) [d : Decidable c] {α : Sort u} {t e : α} (h : t = e) :
    @ite α c d t e = t :=
  match d with
  | is_true hc => rfl
  | is_false hnc => Eq.symm h
#align if_eq_of_eq if_eq_of_eq
-/

#print eq_true_of_and_eq_true_left /-
theorem eq_true_of_and_eq_true_left {a b : Prop} (h : (a ∧ b) = True) : a = True :=
  eq_true (And.left (of_eq_true h))
#align eq_true_of_and_eq_true_left eq_true_of_and_eq_true_left
-/

#print eq_true_of_and_eq_true_right /-
theorem eq_true_of_and_eq_true_right {a b : Prop} (h : (a ∧ b) = True) : b = True :=
  eq_true (And.right (of_eq_true h))
#align eq_true_of_and_eq_true_right eq_true_of_and_eq_true_right
-/

#print eq_false_of_or_eq_false_left /-
theorem eq_false_of_or_eq_false_left {a b : Prop} (h : (a ∨ b) = False) : a = False :=
  eq_false fun ha => False.elim (Eq.mp h (Or.inl ha))
#align eq_false_of_or_eq_false_left eq_false_of_or_eq_false_left
-/

#print eq_false_of_or_eq_false_right /-
theorem eq_false_of_or_eq_false_right {a b : Prop} (h : (a ∨ b) = False) : b = False :=
  eq_false fun hb => False.elim (Eq.mp h (Or.inr hb))
#align eq_false_of_or_eq_false_right eq_false_of_or_eq_false_right
-/

#print eq_false_of_not_eq_true /-
theorem eq_false_of_not_eq_true {a : Prop} (h : Not a = True) : a = False :=
  eq_false fun ha => absurd ha (Eq.mpr h trivial)
#align eq_false_of_not_eq_true eq_false_of_not_eq_true
-/

#print eq_true_of_not_eq_false /-
/-- Remark: the congruence closure module will only use this lemma if
   cc_config.em is tt. -/
theorem eq_true_of_not_eq_false {a : Prop} (h : Not a = False) : a = True :=
  eq_true (by_contradiction fun hna => Eq.mp h hna)
#align eq_true_of_not_eq_false eq_true_of_not_eq_false
-/

#print ne_of_eq_of_ne /-
theorem ne_of_eq_of_ne {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b ≠ c) : a ≠ c :=
  h₁.symm ▸ h₂
#align ne_of_eq_of_ne ne_of_eq_of_ne
-/

#print ne_of_ne_of_eq /-
theorem ne_of_ne_of_eq {α : Sort u} {a b c : α} (h₁ : a ≠ b) (h₂ : b = c) : a ≠ c :=
  h₂ ▸ h₁
#align ne_of_ne_of_eq ne_of_ne_of_eq
-/


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

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

/-!
# Intervals in Lattices

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

In this file, we provide instances of lattice structures on intervals within lattices.
Some of them depend on the order of the endpoints of the interval, and thus are not made
global instances. These are probably not all of the lattice instances that could be placed on these
intervals, but more can be added easily along the same lines when needed.

## Main definitions
In the following, `*` can represent either `c`, `o`, or `i`.
  * `set.Ic*.order_bot`
  * `set.Ii*.semillatice_inf`
  * `set.I*c.order_top`
  * `set.I*c.semillatice_inf`
  * `set.I**.lattice`
  * `set.Iic.bounded_order`, within an `order_bot`
  * `set.Ici.bounded_order`, within an `order_top`

-/

variable {α : Type _}

namespace Set

namespace Ico

instance [SemilatticeInf α] {a b : α} : SemilatticeInf (Ico a b) :=
  Subtype.semilatticeInf fun x y hx hy => ⟨le_inf hx.1 hy.1, lt_of_le_of_lt inf_le_left hx.2⟩

#print Set.Ico.orderBot /-
/-- `Ico a b` has a bottom element whenever `a < b`. -/
@[reducible]
protected def orderBot [PartialOrder α] {a b : α} (h : a < b) : OrderBot (Ico a b) :=
  (isLeast_Ico h).OrderBot
#align set.Ico.order_bot Set.Ico.orderBot
-/

end Ico

namespace Iio

instance [SemilatticeInf α] {a : α} : SemilatticeInf (Iio a) :=
  Subtype.semilatticeInf fun x y hx hy => lt_of_le_of_lt inf_le_left hx

end Iio

namespace Ioc

instance [SemilatticeSup α] {a b : α} : SemilatticeSup (Ioc a b) :=
  Subtype.semilatticeSup fun x y hx hy => ⟨lt_of_lt_of_le hx.1 le_sup_left, sup_le hx.2 hy.2⟩

#print Set.Ioc.orderTop /-
/-- `Ioc a b` has a top element whenever `a < b`. -/
@[reducible]
protected def orderTop [PartialOrder α] {a b : α} (h : a < b) : OrderTop (Ioc a b) :=
  (isGreatest_Ioc h).OrderTop
#align set.Ioc.order_top Set.Ioc.orderTop
-/

end Ioc

namespace Ioi

instance [SemilatticeSup α] {a : α} : SemilatticeSup (Ioi a) :=
  Subtype.semilatticeSup fun x y hx hy => lt_of_lt_of_le hx le_sup_left

end Ioi

namespace Iic

instance [SemilatticeInf α] {a : α} : SemilatticeInf (Iic a) :=
  Subtype.semilatticeInf fun x y hx hy => le_trans inf_le_left hx

instance [SemilatticeSup α] {a : α} : SemilatticeSup (Iic a) :=
  Subtype.semilatticeSup fun x y hx hy => sup_le hx hy

instance [Lattice α] {a : α} : Lattice (Iic a) :=
  { Iic.semilatticeInf, Iic.semilatticeSup with }

instance [Preorder α] {a : α} : OrderTop (Iic a)
    where
  top := ⟨a, le_refl a⟩
  le_top x := x.Prop

/- warning: set.Iic.coe_top -> Set.Iic.coe_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] {a : α}, Eq.{succ u1} α ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (coeSubtype.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Iic.{u1} α _inst_1 a)))))) (Top.top.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) (OrderTop.toHasTop.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) (Subtype.hasLe.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Iic.{u1} α _inst_1 a))) (Set.Iic.orderTop.{u1} α _inst_1 a)))) a
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] {a : α}, Eq.{succ u1} α (Subtype.val.{succ u1} α (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Iic.{u1} α _inst_1 a)) (Top.top.{u1} (Set.Elem.{u1} α (Set.Iic.{u1} α _inst_1 a)) (OrderTop.toTop.{u1} (Set.Elem.{u1} α (Set.Iic.{u1} α _inst_1 a)) (Subtype.le.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Iic.{u1} α _inst_1 a))) (Set.Iic.orderTop.{u1} α _inst_1 a)))) a
Case conversion may be inaccurate. Consider using '#align set.Iic.coe_top Set.Iic.coe_topₓ'. -/
@[simp]
theorem coe_top [Preorder α] {a : α} : ↑(⊤ : Iic a) = a :=
  rfl
#align set.Iic.coe_top Set.Iic.coe_top

instance [Preorder α] [OrderBot α] {a : α} : OrderBot (Iic a)
    where
  bot := ⟨⊥, bot_le⟩
  bot_le := fun ⟨_, _⟩ => Subtype.mk_le_mk.2 bot_le

/- warning: set.Iic.coe_bot -> Set.Iic.coe_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α _inst_1)] {a : α}, Eq.{succ u1} α ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) α (coeSubtype.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Iic.{u1} α _inst_1 a)))))) (Bot.bot.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) (OrderBot.toHasBot.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Iic.{u1} α _inst_1 a)) (Subtype.hasLe.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Iic.{u1} α _inst_1 a))) (Set.Iic.orderBot.{u1} α _inst_1 _inst_2 a)))) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α _inst_1)] {a : α}, Eq.{succ u1} α (Subtype.val.{succ u1} α (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Iic.{u1} α _inst_1 a)) (Bot.bot.{u1} (Set.Elem.{u1} α (Set.Iic.{u1} α _inst_1 a)) (OrderBot.toBot.{u1} (Set.Elem.{u1} α (Set.Iic.{u1} α _inst_1 a)) (Subtype.le.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Iic.{u1} α _inst_1 a))) (Set.Iic.orderBot.{u1} α _inst_1 _inst_2 a)))) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
Case conversion may be inaccurate. Consider using '#align set.Iic.coe_bot Set.Iic.coe_botₓ'. -/
@[simp]
theorem coe_bot [Preorder α] [OrderBot α] {a : α} : ↑(⊥ : Iic a) = (⊥ : α) :=
  rfl
#align set.Iic.coe_bot Set.Iic.coe_bot

instance [Preorder α] [OrderBot α] {a : α} : BoundedOrder (Iic a) :=
  { Iic.orderTop, Iic.orderBot with }

end Iic

namespace Ici

instance [SemilatticeInf α] {a : α} : SemilatticeInf (Ici a) :=
  Subtype.semilatticeInf fun x y hx hy => le_inf hx hy

instance [SemilatticeSup α] {a : α} : SemilatticeSup (Ici a) :=
  Subtype.semilatticeSup fun x y hx hy => le_trans hx le_sup_left

instance [Lattice α] {a : α} : Lattice (Ici a) :=
  { Ici.semilatticeInf, Ici.semilatticeSup with }

instance [DistribLattice α] {a : α} : DistribLattice (Ici a) :=
  { Ici.lattice with le_sup_inf := fun a b c => le_sup_inf }

instance [Preorder α] {a : α} : OrderBot (Ici a)
    where
  bot := ⟨a, le_refl a⟩
  bot_le x := x.Prop

/- warning: set.Ici.coe_bot -> Set.Ici.coe_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] {a : α}, Eq.{succ u1} α ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (coeSubtype.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Ici.{u1} α _inst_1 a)))))) (Bot.bot.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) (OrderBot.toHasBot.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) (Subtype.hasLe.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Ici.{u1} α _inst_1 a))) (Set.Ici.orderBot.{u1} α _inst_1 a)))) a
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] {a : α}, Eq.{succ u1} α (Subtype.val.{succ u1} α (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Ici.{u1} α _inst_1 a)) (Bot.bot.{u1} (Set.Elem.{u1} α (Set.Ici.{u1} α _inst_1 a)) (OrderBot.toBot.{u1} (Set.Elem.{u1} α (Set.Ici.{u1} α _inst_1 a)) (Subtype.le.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Ici.{u1} α _inst_1 a))) (Set.Ici.orderBot.{u1} α _inst_1 a)))) a
Case conversion may be inaccurate. Consider using '#align set.Ici.coe_bot Set.Ici.coe_botₓ'. -/
@[simp]
theorem coe_bot [Preorder α] {a : α} : ↑(⊥ : Ici a) = a :=
  rfl
#align set.Ici.coe_bot Set.Ici.coe_bot

instance [Preorder α] [OrderTop α] {a : α} : OrderTop (Ici a)
    where
  top := ⟨⊤, le_top⟩
  le_top := fun ⟨_, _⟩ => Subtype.mk_le_mk.2 le_top

/- warning: set.Ici.coe_top -> Set.Ici.coe_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α _inst_1)] {a : α}, Eq.{succ u1} α ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) α (coeSubtype.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Ici.{u1} α _inst_1 a)))))) (Top.top.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) (OrderTop.toHasTop.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Ici.{u1} α _inst_1 a)) (Subtype.hasLe.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Ici.{u1} α _inst_1 a))) (Set.Ici.orderTop.{u1} α _inst_1 _inst_2 a)))) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α _inst_1)] {a : α}, Eq.{succ u1} α (Subtype.val.{succ u1} α (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Ici.{u1} α _inst_1 a)) (Top.top.{u1} (Set.Elem.{u1} α (Set.Ici.{u1} α _inst_1 a)) (OrderTop.toTop.{u1} (Set.Elem.{u1} α (Set.Ici.{u1} α _inst_1 a)) (Subtype.le.{u1} α (Preorder.toLE.{u1} α _inst_1) (fun (x : α) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (Set.Ici.{u1} α _inst_1 a))) (Set.Ici.orderTop.{u1} α _inst_1 _inst_2 a)))) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α _inst_1) _inst_2))
Case conversion may be inaccurate. Consider using '#align set.Ici.coe_top Set.Ici.coe_topₓ'. -/
@[simp]
theorem coe_top [Preorder α] [OrderTop α] {a : α} : ↑(⊤ : Ici a) = (⊤ : α) :=
  rfl
#align set.Ici.coe_top Set.Ici.coe_top

instance [Preorder α] [OrderTop α] {a : α} : BoundedOrder (Ici a) :=
  { Ici.orderTop, Ici.orderBot with }

end Ici

namespace Icc

instance [SemilatticeInf α] {a b : α} : SemilatticeInf (Icc a b) :=
  Subtype.semilatticeInf fun x y hx hy => ⟨le_inf hx.1 hy.1, le_trans inf_le_left hx.2⟩

instance [SemilatticeSup α] {a b : α} : SemilatticeSup (Icc a b) :=
  Subtype.semilatticeSup fun x y hx hy => ⟨le_trans hx.1 le_sup_left, sup_le hx.2 hy.2⟩

instance [Lattice α] {a b : α} : Lattice (Icc a b) :=
  { Icc.semilatticeInf, Icc.semilatticeSup with }

#print Set.Icc.orderBot /-
/-- `Icc a b` has a bottom element whenever `a ≤ b`. -/
@[reducible]
protected def orderBot [Preorder α] {a b : α} (h : a ≤ b) : OrderBot (Icc a b) :=
  (isLeast_Icc h).OrderBot
#align set.Icc.order_bot Set.Icc.orderBot
-/

#print Set.Icc.orderTop /-
/-- `Icc a b` has a top element whenever `a ≤ b`. -/
@[reducible]
protected def orderTop [Preorder α] {a b : α} (h : a ≤ b) : OrderTop (Icc a b) :=
  (isGreatest_Icc h).OrderTop
#align set.Icc.order_top Set.Icc.orderTop
-/

#print Set.Icc.boundedOrder /-
/-- `Icc a b` is a `bounded_order` whenever `a ≤ b`. -/
@[reducible]
protected def boundedOrder [Preorder α] {a b : α} (h : a ≤ b) : BoundedOrder (Icc a b) :=
  { Icc.orderTop h, Icc.orderBot h with }
#align set.Icc.bounded_order Set.Icc.boundedOrder
-/

end Icc

end Set