import combinatorics.simple_graph.clique
import combinatorics.simple_graph.degree_sum
import data.finset.basic
import data.nat.basic
import tactic.core
import algebra.big_operators

-- local imports
import fedges
import nbhd_res
import clique_free_sets
import misc_finset
import multipartite
import turanpartition
import induced

open finset nat turanpartition

open_locale big_operators

namespace simple_graph

variables {t n : ℕ}
variables {α : Type*} (G H : simple_graph α)[fintype α][nonempty α][decidable_eq α][decidable_rel G.adj][decidable_rel H.adj]
include G

---for any (t+2)-clique free set there is a partition into B, a (t+1)-clique free set and A\B
-- such that e(A)+e(A\B) ≤ e(B) + |B|(|A|-|B|)
lemma furedi_help : ∀A:finset α, G.clique_free_set A (t+2) → ∃B:finset α, B ⊆ A ∧ G.clique_free_set B (t+1) ∧
∑v in A, G.deg_res v A + ∑ v in (A\B), G.deg_res v (A\B) ≤ ∑ v in B, G.deg_res v B + 2*B.card * (A\B).card:=
begin
  cases nat.eq_zero_or_pos t with ht,{
  intros A hA,rw ht at *, rw zero_add at *,
----- t = 0 need to check that ∅ is not a 1-clique.
  refine ⟨∅,⟨empty_subset A,(G.clique_free_empty (by norm_num: 0 <1)),_⟩⟩,
  rw [sdiff_empty, card_empty, mul_zero,zero_mul, sum_empty, zero_add,G.two_clique_free_sum hA]},{
----- 0 < t case
  intros A hA, by_cases hnem: A.nonempty,{
    obtain ⟨x,hxA,hxM⟩:=G.exists_max_res_deg_vertex hnem, -- get a vert x of max res deg in A
    set hBA:= (G.sub_res_nbhd_A x A),
    set B:=(G.nbhd_res x A) with hB,-- Let B be the res nbhd of the vertex x of max deg_A
    refine ⟨B, ⟨hBA,(G.t_clique_free hA hxA),_⟩⟩,
    rw [G.deg_res_add_sum hBA, G.sum_sdf hBA B, add_assoc],
    rw [G.sum_sdf hBA (A\B),G.bip_count hBA,← G.deg_res_add_sum hBA ],
    rw ← hB, rw ← add_assoc, ring_nf,
    apply add_le_add_left _ (∑ v in B, G.deg_res v B ),
    rw add_comm, rw add_assoc, nth_rewrite 1 add_comm,
    rw ← G.deg_res_add_sum hBA, ring_nf,rw mul_assoc,
    refine mul_le_mul' (by norm_num) _,
    apply le_trans (G.max_deg_res_sum_le (sdiff_subset A B)) _,
    rw [hxM,deg_res],},
    {rw not_nonempty_iff_eq_empty at hnem,
    refine ⟨∅,⟨empty_subset A,(G.clique_free_empty (by norm_num: 0 <t+1)),_⟩⟩,
    rw [sdiff_empty, card_empty, mul_zero,zero_mul, sum_empty, zero_add,hnem,sum_empty],}},
end

-- Putting together the deg counts of G induced on a larger partition (M with C inserted).
-- Counting degrees sums over the parts of the larger partition is what you expect
-- ie e(G[M_0])+ .. +e(G[M_t])+e(G[C]) = e(G[M'_0])+...+e(G[M'_{t+1}])
lemma internal_count {M: multi_part α} {C : finset α} (h: disjoint M.A C):
 ∑ i in range(M.t+1),∑ v in (M.P i), G.deg_res v (M.P i) + ∑ v in C, G.deg_res v C  =
∑ i in range((insert M h).t+1), ∑ v in ((insert M h).P i), G.deg_res v ((insert M h).P i):=
begin
  simp [insert_t, insert_P,ite_not],
  have  ru:range((M.t+1)+1)=range(M.t+1) ∪ {M.t+1},{
    rw range_succ, rw union_comm, rw insert_eq _,},
  have nm:(M.t+1)∉(range(M.t+1)):=not_mem_range_self,
  have rd: disjoint (range(M.t+1)) {M.t+1}:= disjoint_singleton_right.mpr nm,
  rw [ru,sum_union rd],simp only [sum_singleton, eq_self_iff_true, if_true],
  apply (add_left_inj _).mpr, apply sum_congr rfl, intros k hk,
  have nm:(M.t+1)∉(range(M.t+1)):=not_mem_range_self,
  have kne: k≠M.t+1,{intro h',rw h' at hk, exact nm hk},
  apply sum_congr, split_ifs,{contradiction},{refl},{
  intros v hv,split_ifs,{contradiction},{refl}},
end

-- Furedi's stability theorem: (t+2)-clique-free set A implies there is a (t+1)-partition of A
-- such that edges in A + edges in parts (counted a second time) ≤ edges in the complete
-- (t+1)-partite graph on same partition
-- implies Turan once we have know how to maximize edges of a complete multi-partite graph
theorem furedi : ∀A:finset α, G.clique_free_set A (t+2) → ∃M:multi_part α, M.A=A ∧ M.t =t ∧
∑v in A, G.deg_res v A + ∑ i in range(M.t+1),∑ v in (M.P i), G.deg_res v (M.P i) ≤ ∑ v in A, (mp M).deg_res v A:=
begin
  induction t with t ht, {rw zero_add,
  intros A ha, use (default_M A 0), refine ⟨rfl,rfl,_⟩, rw G.two_clique_free_sum ha,
  rw zero_add, unfold default_M, dsimp,simp, apply sum_le_sum,
  intros x hx, rw G.two_clique_free ha x hx,exact zero_le _ },
  --- t.succ case
  {intros A ha, obtain⟨B,hBa,hBc,hBs⟩:=G.furedi_help A ha,
  have hAsd:=union_sdiff_of_subset hBa,
  obtain ⟨M,Ma,Mt,Ms⟩:=ht B hBc,
  have dAB:disjoint M.A (A\B), {rw Ma, exact disjoint_sdiff,},
  set H: simple_graph α:= (mp (insert M dAB)),
  use (insert M dAB), refine ⟨_,_,_⟩,{
  rw [insert_AB, Ma], exact union_sdiff_of_subset hBa}, {rwa [insert_t, Mt]},{
  --- so we now have the new partition and "just" need to check the degree sum bound..
  have mpc:=mp_count M dAB, rw [insert_AB, Ma , hAsd] at mpc,
  -- need to sort out the sum over parts in the larger graph
  rw ←  mpc, rw ← G.internal_count dAB, linarith},},
end

end simple_graph


import game.sets.L01defs

--NOTE: the recursive import from previous world breaks run_cmd add_interactive below??
--I think the problem comes from sup_inf.rat_complete
--So I will import sup_inf in the next level

import data.real.basic
import tactic.linarith

namespace xena -- hide
notation `|` x `|` := abs x -- hide

lemma zero_of_abs_lt_all (x : ℝ) (h : ∀ ε > 0, |x| < ε) : x = 0 :=
eq_zero_of_abs_eq_zero $ eq_of_le_of_forall_le_of_dense (abs_nonneg x) $ λ ε ε_pos, le_of_lt (h ε ε_pos)

-- begin hide
-- The next few things should be hidden
@[user_attribute]
meta def ineq_rules : user_attribute :=
{ name := `ineq_rules,
  descr := "lemmas usable to prove inequalities" }

attribute [ineq_rules] add_lt_add le_max_left le_max_right

meta def inequality := `[linarith <|> apply_rules ineq_rules]
run_cmd add_interactive [`inequality]
-- end of scary things
-- end hide

-- World name : Sequences and limits

/-
# Chapter 3 : Sequences and limits

# Level 1 : Introduction to sequences.

Lean's natural numbers start at zero, so it is convenient to let our sequences start from the zeroth term.
In other words, a sequence of reals will be $a_0, a_1, a_2, \ldots$.
-/

/-
Let's just step back for a minute and think about what a sequence really *is*.
If $n$ is a natural number then $a_n$ is a real number,
so $n\mapsto a_n$ is actually a function from natural numbers to real numbers.
If we just call this function $a$ then the $n$th term in the sequence
will be called `a(n)` or `a n` in Lean, rather than $a_n$, but this is OK.

The key definition we want is the concept of a limit of a sequence.
-/

definition is_limit (a : ℕ → ℝ) (α : ℝ) :=
  ∀ ε : ℝ, 0 < ε → ∃ N : ℕ, ∀ n : ℕ, N ≤ n → |a n - α| < ε

/-
Let's now prove the basic fact that a sequence has at most one limit.
-/

/- Lemma
If $a_n \to \ell$ and $a_n \to m$ then $\ell = m$.
-/
lemma limit.unique (a : ℕ → ℝ) (l m : ℝ) (hl : is_limit a l) (hm : is_limit a m) : l = m :=
begin
  wlog h : l ≤ m,
  rw le_iff_lt_or_eq at h,
  cases h,
    exfalso,
    generalize h : (m - l) / 2 = ε,
    have hε : 0 < ε,
      {inequality},
    cases (hl ε hε) with L hL,
    cases (hm ε hε) with M hM,
    have hL' := hL (max L M) (le_max_left _ _),
    have hM' := hM (max L M) (le_max_right _ _),
    rw abs_lt at hL',
    rw abs_lt at hM',
    cases hL', cases hM',
    linarith,
  assumption,

end

end xena -- hide


/-
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 algebra.free_monoid.count
! leanprover-community/mathlib commit a2d2e18906e2b62627646b5d5be856e6a642062f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.FreeMonoid.Basic
import Mathlib.Data.List.Count

/-!
# `List.count` as a bundled homomorphism

In this file we define `FreeMonoid.countp`, `FreeMonoid.count`, `FreeAddMonoid.countp`, and
`FreeAddMonoid.count`. These are `List.countp` and `List.count` bundled as multiplicative and
additive homomorphisms from `FreeMonoid` and `FreeAddMonoid`.

We do not use `to_additive` because it can't map `Multiplicative ℕ` to `ℕ`.
-/

variable {α : Type _} (p : α → Prop) [DecidablePred p]

namespace FreeAddMonoid

/-- `List.countp` as a bundled additive monoid homomorphism. -/
def countp : FreeAddMonoid α →+ ℕ where
  toFun := List.countp p
  map_zero' := List.countp_nil _
  map_add' := List.countp_append _
#align free_add_monoid.countp FreeAddMonoid.countp

theorem countp_of (x : α): countp p (of x) = if p x = true then 1 else 0 := by
  simp [countp, List.countp, List.countp.go]
#align free_add_monoid.countp_of FreeAddMonoid.countp_of

/-- `List.count` as a bundled additive monoid homomorphism. -/
-- Porting note: was (x = ·)
def count [DecidableEq α] (x : α) : FreeAddMonoid α →+ ℕ := countp (· = x)
#align free_add_monoid.count FreeAddMonoid.count

theorem count_of [DecidableEq α] (x y : α) : count x (of y) = (Pi.single x 1 : α → ℕ) y := by
  simp [Pi.single, Function.update, count, countp, List.countp, List.countp.go,
    Bool.beq_eq_decide_eq]
#align free_add_monoid.count_of FreeAddMonoid.count_of

theorem count_apply [DecidableEq α] (x : α) (l : FreeAddMonoid α) : count x l = List.count x l :=
  rfl
#align free_add_monoid.count_apply FreeAddMonoid.count_apply

end FreeAddMonoid

namespace FreeMonoid

/-- `list.countp` as a bundled multiplicative monoid homomorphism. -/
def countp : FreeMonoid α →* Multiplicative ℕ :=
    AddMonoidHom.toMultiplicative (FreeAddMonoid.countp p)
#align free_monoid.countp FreeMonoid.countp

theorem countp_of' (x : α) :
    countp p (of x) = if p x then Multiplicative.ofAdd 1 else Multiplicative.ofAdd 0 := by
    erw [FreeAddMonoid.countp_of]
    simp only [eq_iff_iff, iff_true, ofAdd_zero]; rfl
#align free_monoid.countp_of' FreeMonoid.countp_of'

theorem countp_of (x : α) : countp p (of x) = if p x then Multiplicative.ofAdd 1 else 1 := by
  rw [countp_of', ofAdd_zero]
#align free_monoid.countp_of FreeMonoid.countp_of

-- `rfl` is not transitive
theorem countp_apply (l : FreeAddMonoid α) : countp p l = Multiplicative.ofAdd (List.countp p l) :=
  rfl
#align free_monoid.countp_apply FreeMonoid.countp_apply

/-- `List.count` as a bundled additive monoid homomorphism. -/
def count [DecidableEq α] (x : α) : FreeMonoid α →* Multiplicative ℕ := countp (· = x)
#align free_monoid.count FreeMonoid.count

theorem count_apply [DecidableEq α] (x : α) (l : FreeAddMonoid α) :
    count x l = Multiplicative.ofAdd (List.count x l) := rfl
#align free_monoid.count_apply FreeMonoid.count_apply

theorem count_of [DecidableEq α] (x y : α) :
    count x (of y) = @Pi.mulSingle α (fun _ => Multiplicative ℕ) _ _ x (Multiplicative.ofAdd 1) y :=
  by simp [count, countp_of, Pi.mulSingle_apply, eq_comm, Bool.beq_eq_decide_eq]
#align free_monoid.count_of FreeMonoid.count_of

end FreeMonoid


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import algebra.hom.aut
import group_theory.group_action.units

/-!
# Group actions applied to various types of group

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

This file contains lemmas about `smul` on `group_with_zero`, and `group`.
-/

open function

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

section mul_action

/-- `monoid.to_mul_action` is faithful on cancellative monoids. -/
@[to_additive /-" `add_monoid.to_add_action` is faithful on additive cancellative monoids. "-/]
instance right_cancel_monoid.to_has_faithful_smul [right_cancel_monoid α] :
  has_faithful_smul α α :=
⟨λ x y h, mul_right_cancel (h 1)⟩

section group
variables [group α] [mul_action α β]

@[simp, to_additive] lemma inv_smul_smul (c : α) (x : β) : c⁻¹ • c • x = x :=
by rw [smul_smul, mul_left_inv, one_smul]

@[simp, to_additive] lemma smul_inv_smul (c : α) (x : β) : c • c⁻¹ • x = x :=
by rw [smul_smul, mul_right_inv, one_smul]

/-- Given an action of a group `α` on `β`, each `g : α` defines a permutation of `β`. -/
@[to_additive, simps] def mul_action.to_perm (a : α) : equiv.perm β :=
⟨λ x, a • x, λ x, a⁻¹ • x, inv_smul_smul a, smul_inv_smul a⟩

/-- Given an action of an additive group `α` on `β`, each `g : α` defines a permutation of `β`. -/
add_decl_doc add_action.to_perm

/-- `mul_action.to_perm` is injective on faithful actions. -/
@[to_additive "`add_action.to_perm` is injective on faithful actions."]
lemma mul_action.to_perm_injective [has_faithful_smul α β] :
  function.injective (mul_action.to_perm : α → equiv.perm β) :=
(show function.injective (equiv.to_fun ∘ mul_action.to_perm), from smul_left_injective').of_comp

variables (α) (β)

/-- Given an action of a group `α` on a set `β`, each `g : α` defines a permutation of `β`. -/
@[simps]
def mul_action.to_perm_hom : α →* equiv.perm β :=
{ to_fun := mul_action.to_perm,
  map_one' := equiv.ext $ one_smul α,
  map_mul' := λ u₁ u₂, equiv.ext $ mul_smul (u₁:α) u₂ }

/-- Given an action of a additive group `α` on a set `β`, each `g : α` defines a permutation of
`β`. -/
@[simps]
def add_action.to_perm_hom (α : Type*) [add_group α] [add_action α β] :
  α →+ additive (equiv.perm β) :=
{ to_fun := λ a, additive.of_mul $ add_action.to_perm a,
  map_zero' := equiv.ext $ zero_vadd α,
  map_add' := λ a₁ a₂, equiv.ext $ add_vadd a₁ a₂ }

/-- The tautological action by `equiv.perm α` on `α`.

This generalizes `function.End.apply_mul_action`.-/
instance equiv.perm.apply_mul_action (α : Type*) : mul_action (equiv.perm α) α :=
{ smul := λ f a, f a,
  one_smul := λ _, rfl,
  mul_smul := λ _ _ _, rfl }

@[simp] protected lemma equiv.perm.smul_def {α : Type*} (f : equiv.perm α) (a : α) : f • a = f a :=
rfl

/-- `equiv.perm.apply_mul_action` is faithful. -/
instance equiv.perm.apply_has_faithful_smul (α : Type*) : has_faithful_smul (equiv.perm α) α :=
⟨λ x y, equiv.ext⟩

variables {α} {β}

@[to_additive] lemma inv_smul_eq_iff {a : α} {x y : β} : a⁻¹ • x = y ↔ x = a • y :=
(mul_action.to_perm a).symm_apply_eq

@[to_additive] lemma eq_inv_smul_iff {a : α} {x y : β} : x = a⁻¹ • y ↔ a • x = y :=
(mul_action.to_perm a).eq_symm_apply

lemma smul_inv [group β] [smul_comm_class α β β] [is_scalar_tower α β β] (c : α) (x : β) :
  (c • x)⁻¹ = c⁻¹ • x⁻¹  :=
by rw [inv_eq_iff_mul_eq_one, smul_mul_smul, mul_right_inv, mul_right_inv, one_smul]

lemma smul_zpow [group β] [smul_comm_class α β β] [is_scalar_tower α β β]
  (c : α) (x : β) (p : ℤ) :
  (c • x) ^ p = c ^ p • x ^ p :=
by { cases p; simp [smul_pow, smul_inv] }

@[simp] lemma commute.smul_right_iff [has_mul β] [smul_comm_class α β β] [is_scalar_tower α β β]
  {a b : β} (r : α) :
  commute a (r • b) ↔ commute a b :=
⟨λ h, inv_smul_smul r b ▸ h.smul_right r⁻¹, λ h, h.smul_right r⟩

@[simp] lemma commute.smul_left_iff [has_mul β] [smul_comm_class α β β] [is_scalar_tower α β β]
  {a b : β} (r : α) :
  commute (r • a) b ↔ commute a b :=
by rw [commute.symm_iff, commute.smul_right_iff, commute.symm_iff]

@[to_additive] protected lemma mul_action.bijective (g : α) : bijective ((•) g : β → β) :=
(mul_action.to_perm g).bijective

@[to_additive] protected lemma mul_action.injective (g : α) : injective ((•) g : β → β) :=
(mul_action.bijective g).injective

@[to_additive] protected lemma mul_action.surjective (g : α) : surjective ((•) g : β → β) :=
(mul_action.bijective g).surjective

@[to_additive] lemma smul_left_cancel (g : α) {x y : β} (h : g • x = g • y) : x = y :=
mul_action.injective g h

@[simp, to_additive] lemma smul_left_cancel_iff (g : α) {x y : β} : g • x = g • y ↔ x = y :=
(mul_action.injective g).eq_iff

@[to_additive] lemma smul_eq_iff_eq_inv_smul (g : α) {x y : β} :
  g • x = y ↔ x = g⁻¹ • y :=
(mul_action.to_perm g).apply_eq_iff_eq_symm_apply

end group

/-- `monoid.to_mul_action` is faithful on nontrivial cancellative monoids with zero. -/
instance cancel_monoid_with_zero.to_has_faithful_smul [cancel_monoid_with_zero α] [nontrivial α] :
  has_faithful_smul α α :=
⟨λ x y h, mul_left_injective₀ one_ne_zero (h 1)⟩

section gwz
variables [group_with_zero α] [mul_action α β] {a : α}

@[simp]
lemma inv_smul_smul₀ {c : α} (hc : c ≠ 0) (x : β) : c⁻¹ • c • x = x :=
inv_smul_smul (units.mk0 c hc) x

@[simp]
lemma smul_inv_smul₀ {c : α} (hc : c ≠ 0) (x : β) : c • c⁻¹ • x = x :=
smul_inv_smul (units.mk0 c hc) x

lemma inv_smul_eq_iff₀ {a : α} (ha : a ≠ 0) {x y : β} : a⁻¹ • x = y ↔ x = a • y :=
(mul_action.to_perm (units.mk0 a ha)).symm_apply_eq

lemma eq_inv_smul_iff₀ {a : α} (ha : a ≠ 0) {x y : β} : x = a⁻¹ • y ↔ a • x = y :=
(mul_action.to_perm (units.mk0 a ha)).eq_symm_apply

@[simp] lemma commute.smul_right_iff₀ [has_mul β] [smul_comm_class α β β] [is_scalar_tower α β β]
  {a b : β} {c : α} (hc : c ≠ 0) :
  commute a (c • b) ↔ commute a b :=
commute.smul_right_iff (units.mk0 c hc)

@[simp] lemma commute.smul_left_iff₀ [has_mul β] [smul_comm_class α β β] [is_scalar_tower α β β]
  {a b : β} {c : α} (hc : c ≠ 0) :
  commute (c • a) b ↔ commute a b :=
commute.smul_left_iff (units.mk0 c hc)

protected lemma mul_action.bijective₀ (ha : a ≠ 0) : bijective ((•) a : β → β) :=
mul_action.bijective $ units.mk0 a ha

protected lemma mul_action.injective₀ (ha : a ≠ 0) : injective ((•) a : β → β) :=
(mul_action.bijective₀ ha).injective

protected lemma mul_action.surjective₀ (ha : a ≠ 0) : surjective ((•) a : β → β) :=
(mul_action.bijective₀ ha).surjective

end gwz

end mul_action

section distrib_mul_action

section group
variables [group α] [add_monoid β] [distrib_mul_action α β]

variables (β)

/-- Each element of the group defines an additive monoid isomorphism.

This is a stronger version of `mul_action.to_perm`. -/
@[simps {simp_rhs := tt}]
def distrib_mul_action.to_add_equiv (x : α) : β ≃+ β :=
{ .. distrib_mul_action.to_add_monoid_hom β x,
  .. mul_action.to_perm_hom α β x }

variables (α β)

/-- Each element of the group defines an additive monoid isomorphism.

This is a stronger version of `mul_action.to_perm_hom`. -/
@[simps]
def distrib_mul_action.to_add_aut : α →* add_aut β :=
{ to_fun := distrib_mul_action.to_add_equiv β,
  map_one' := add_equiv.ext (one_smul _),
  map_mul' := λ a₁ a₂, add_equiv.ext (mul_smul _ _) }

variables {α β}

theorem smul_eq_zero_iff_eq (a : α) {x : β} : a • x = 0 ↔ x = 0 :=
⟨λ h, by rw [← inv_smul_smul a x, h, smul_zero], λ h, h.symm ▸ smul_zero _⟩

theorem smul_ne_zero_iff_ne (a : α) {x : β} : a • x ≠ 0 ↔ x ≠ 0 :=
not_congr $ smul_eq_zero_iff_eq a

end group

section gwz
variables [group_with_zero α] [add_monoid β] [distrib_mul_action α β]

theorem smul_eq_zero_iff_eq' {a : α} (ha : a ≠ 0) {x : β} : a • x = 0 ↔ x = 0 :=
show units.mk0 a ha • x = 0 ↔ x = 0, from smul_eq_zero_iff_eq _

theorem smul_ne_zero_iff_ne' {a : α} (ha : a ≠ 0) {x : β} : a • x ≠ 0 ↔ x ≠ 0 :=
show units.mk0 a ha • x ≠ 0 ↔ x ≠ 0, from smul_ne_zero_iff_ne _

end gwz

end distrib_mul_action

section mul_distrib_mul_action
variables [group α] [monoid β] [mul_distrib_mul_action α β]

variables (β)

/-- Each element of the group defines a multiplicative monoid isomorphism.

This is a stronger version of `mul_action.to_perm`. -/
@[simps {simp_rhs := tt}]
def mul_distrib_mul_action.to_mul_equiv (x : α) : β ≃* β :=
{ .. mul_distrib_mul_action.to_monoid_hom β x,
  .. mul_action.to_perm_hom α β x }

variables (α β)

/-- Each element of the group defines an multiplicative monoid isomorphism.

This is a stronger version of `mul_action.to_perm_hom`. -/
@[simps]
def mul_distrib_mul_action.to_mul_aut : α →* mul_aut β :=
{ to_fun := mul_distrib_mul_action.to_mul_equiv β,
  map_one' := mul_equiv.ext (one_smul _),
  map_mul' := λ a₁ a₂, mul_equiv.ext (mul_smul _ _) }

variables {α β}

end mul_distrib_mul_action

section arrow

/-- If `G` acts on `A`, then it acts also on `A → B`, by `(g • F) a = F (g⁻¹ • a)`. -/
@[to_additive arrow_add_action "If `G` acts on `A`, then it acts also on `A → B`, by
`(g +ᵥ F) a = F (g⁻¹ +ᵥ a)`", simps]
def arrow_action {G A B : Type*} [division_monoid G] [mul_action G A] : mul_action G (A → B) :=
{ smul := λ g F a, F (g⁻¹ • a),
  one_smul := by { intro, simp only [inv_one, one_smul] },
  mul_smul := by { intros, simp only [mul_smul, mul_inv_rev] } }

local attribute [instance] arrow_action

/-- When `B` is a monoid, `arrow_action` is additionally a `mul_distrib_mul_action`. -/
def arrow_mul_distrib_mul_action {G A B : Type*} [group G] [mul_action G A] [monoid B] :
  mul_distrib_mul_action G (A → B) :=
{ smul_one := λ g, rfl,
  smul_mul := λ g f₁ f₂, rfl }

local attribute [instance] arrow_mul_distrib_mul_action

/-- Given groups `G H` with `G` acting on `A`, `G` acts by
  multiplicative automorphisms on `A → H`. -/
@[simps] def mul_aut_arrow {G A H} [group G] [mul_action G A] [monoid H] : G →* mul_aut (A → H) :=
mul_distrib_mul_action.to_mul_aut _ _

end arrow

namespace is_unit

section mul_action
variables [monoid α] [mul_action α β]

@[to_additive] lemma smul_left_cancel {a : α} (ha : is_unit a) {x y : β} :
  a • x = a • y ↔ x = y :=
let ⟨u, hu⟩ := ha in hu ▸ smul_left_cancel_iff u

end mul_action

section distrib_mul_action
variables [monoid α] [add_monoid β] [distrib_mul_action α β]

@[simp] theorem smul_eq_zero {u : α} (hu : is_unit u) {x : β} :
  u • x = 0 ↔ x = 0 :=
exists.elim hu $ λ u hu, hu ▸ show u • x = 0 ↔ x = 0, from smul_eq_zero_iff_eq u

end distrib_mul_action

end is_unit

section smul

variables [group α] [monoid β]

@[simp] lemma is_unit_smul_iff [mul_action α β] [smul_comm_class α β β] [is_scalar_tower α β β]
  (g : α) (m : β) : is_unit (g • m) ↔ is_unit m :=
⟨λ h, inv_smul_smul g m ▸ h.smul g⁻¹, is_unit.smul g⟩

lemma is_unit.smul_sub_iff_sub_inv_smul
  [add_group β] [distrib_mul_action α β] [is_scalar_tower α β β] [smul_comm_class α β β]
  (r : α) (a : β) : is_unit (r • 1 - a) ↔ is_unit (1 - r⁻¹ • a) :=
by rw [←is_unit_smul_iff r (1 - r⁻¹ • a), smul_sub, smul_inv_smul]

end smul


-- Высказывания, связки и аксиомы

namespace props
  constant and'  : Prop → Prop → Prop     -- все высказывания в Lean живут в специальной вселенной Prop
                                          -- таким образом различными операциями над высказываниями являются
                                          -- просто функции над Prop

  constant or'   : Prop → Prop → Prop
  constant not'  : Prop → Prop
  constant impl' : Prop → Prop → Prop

  variables a b c : Prop

  #check and' a (or' b c) -- Prop         -- поведение таких функций абсолютно аналогично населяющим вселенные Type u

  constant Proof : Prop → Type            -- введем тип доказательств: для любого (a : Prop), Proof a будет содержать доказательство a

  constant and_comm' : Π a b : Prop,      -- тогда аксиомы - это просто константы типа Proof от некоторого аксиоматичного высказывания
                        Proof (impl' (and' a b) (and' b a))

  #check and_comm' a b -- Proof (impl' (and' a b) (and' b a)) -- "доказывает" или устанавливает аксиоматичность (a ∧ b) → (b ∧ a)

  constant modus_ponens : Π a b : Prop,   -- задает правило Modes Ponens, выводящее b из (a → b) и истиности a
                            Proof (impl' a b) → Proof a → Proof b
end props

-- Теоремы

namespace theorems
  constants p q : Prop

  theorem t1 : p → q → p :=               -- для красоты записи доказательств, функции над Prop называют теоремами
    λ (hp : p), λ (hq : q), hp            -- изоморфизм Карри-Говарда говорит, что если тип обитаем, то это то же, что
                                          -- изоморфное ему высказывание истино; здесь элементарно доказывается первая аксиома
                                          -- Гильберта

  theorem t1' : p → q → p :=
    assume hp : p,                        -- для красоты записи теорем вводится синтаксический сахар:
    assume hq : q,                        -- assume x : α == λ (x : α)
    hp                                    -- assume - предположим, допустим, рассмотрим

  theorem t1'' : p → q → p :=
    assume hp : p,
    assume hq : q,
    show p, from hp                       -- так как в результате требуется доказать p, вводится специальный сахар
                                          -- show {type}, from {expr}

  lemma l1 : p → q → p :=                 -- слово theorem можно заменить на lemma в любом месте
    assume hp : p,
    assume hq : q,
    show p, from hp

  lemma l1a (hp : p) (hq : q) : p := hp   -- аналогично функциям, аргументы можно явно поименовать

  axiom hp : p                            -- еще один синтаксический сахар - слово axiom, которым можно заменять constant

  theorem t2 : q → p := t1 hp             -- леммы и теоремы можно так же применять к аргументам для получения нужных значений

  theorem t1_common : ∀ (p q : Prop),     -- наша оригинальная теорема работает только для конкретных p и q
                      p → q → p :=        -- её можно записать для любых высказываний, введя квантор ∀ (\forall),
    assume p : Prop,                      -- который является полной аналогией Π для Prop
    assume q : Prop,                      -- в этом случае также требуется вводить через λ-абстракцию/assume сами высказывания
    assume hp : p,
    assume hq : q,
    show p, from hp

  variables p' q' r' s' : Prop            -- как и в функциях, все можно повыносить в общие переменные

  theorem t1_common_var : p' → q' → p' := -- поведение теорем и лемм в этом случае также полностью аналогично функциям
    assume hp : p',
    assume hq : q',
    show p', from hp

  #check t1_common p' q' -- p' → q' → p'  -- обобщение нашей теоремы позволяет использовать её на любых высказываниях
  #check t1_common r' s' -- r' → s' → r'
  #check t1_common (p' → q') (s' → r') -- (p' → q') → (s' → r') → p' → q'

  #check t1_common p q hp -- q → p        -- подстановка аксиомы в теорему как и прежде позволила выдать новое утверждение

  theorem t3 : ∀ {p q r : Prop},          -- в теоремах также можно использовать неявные аргументы
                (q → r) → (p → q) →
                p → r :=
    assume p q r : Prop,                  -- как и в функциях, их нужно вводить, и, кстати, assume тоже поддерживает сахар
    assume h₁ : q → r,                    -- для множества переменных одного типа
    assume h₂ : p → q,                    -- красивые нижние индексы получаются через \_{символ} (например, h\_1 или h\_2)
    assume h₃ : p,
    show r, from h₁ (h₂ h₃)

  #check @t3 -- ∀ {p q r : Prop}, (q → r) → (p → q) → p → r

  example : p → q → p :=                  -- чтоб доказать что-то, но не засорять пространство имен, можно воспользоваться
    assume hp : p,                        -- "примером", вводимым командой example
    assume hq : q,
    show p, from hp

  example (hp : p) (hq : q) : p := hp     -- примеры, как и все прочее поддерживают передачу именованных аргументов

  example : ∀ {p q r : Prop},
              (q → r) → (p → q) →
              p → r :=
    assume p q r : Prop,
    assume hqr : q → r,
    assume hpq : p → q,
    assume hp  : p,                       -- можно установить промежуточное утверждение благодаря конструкции
    have hq : q, from hpq hp,             -- have {var : type}, from {expr}, аналогичной show
    show r, from hqr hq                   -- далее его можно удобно использовать по ходу доказательства
                                          -- по сути сахар (have x : p, from e, t) превращается в
                                          -- (λ (x : p), t) e

   example : ∀ {p q r : Prop},
              (q → r) → (p → q) →
              p → r :=
    assume p q r : Prop,
    assume hqr : q → r,
    assume hpq : p → q,
    assume hp  : p,
    have q, from hpq hp,                  -- have может быть и анонимным (только тип, без терма)
    show r, from hqr this                 -- чтоб использовать такой have можно применять слово this, оно обращается
                                          -- к последнему have; в данном случае (this : q)

  example : ∀ {p q r : Prop},             -- возможна также конструкция suffices to show, строящая утверждение на том,
              (q → r) → (p → q) →         -- что достаточно доказать подцель, чтоб получить цель (на мой взгляд, не очень удобно)
              p → r :=                    -- синтаксические просто переписывается в (have x : p, from e, t)
    assume p q r : Prop,
    assume hqr : q → r,
    assume hpq : p → q,
    assume hp  : p,
    suffices hq : q, from hqr hq,         -- читаем: достаточно доказать найти доказательсто hq для q, чтоб дальше
    show q, from hpq hp                   -- показать искомое через hqr hq, что мы и делаем доказывая q через hpq hp

  example : ∀ {p q r : Prop},             -- полная дешугаризация нашей теоремы будет выглядеть жутковато,
              (q → r) → (p → q) →         -- хотя доказывается в ней ровно то же самое
              p → r :=
    λ p : Prop, λ q : Prop, λ r : Prop,
    λ hpr : q → r, λ hpq : p → q, λ hp : p,
    (λ hq : q, hpr hq) (hpq hp)
end theorems

-- Логика в стандартной библиотеке

namespace stdlib_logic
  variables a b : Prop

  #check a → b → a ∧ b  -- Prop           -- связки → (\r), ∧ (\and), ∨ (\or), ¬ (\not), ↔ (\iff),
  #check ¬a → a ↔ false -- Prop           -- а также константы true и false уже определены в стандартной библиотеке
  #check a ∨ b → b ∨ a  -- Prop
end stdlib_logic

-- and

namespace stdlib_and
  variables p q : Prop

  example (hp : p) (hq : q) : p ∧ q :=    -- в стандартной библиотеке определено огромное количество полезных теорем и лемм,
    and.intro hp hq                       -- которые можно и нужно активно использовать

  example : p ∧ q → p :=
    assume hpq : p ∧ q,
    show p, from and.elim_left hpq

  example : p ∧ q → q :=
    assume hpq : p ∧ q,
    show q, from and.elim_right hpq

  #check @and.intro      -- ∀ {a b : Prop}, a → b → a ∧ b
  #check @and.elim_left  -- ∀ {a b : Prop}, a ∧ b → a
  #check @and.elim_right -- ∀ {a b : Prop}, a ∧ b → b

  example : p ∧ q → q ∧ p :=              -- многие вещи встречаются в стандартной библиотеке по нескольку раз для удобства
    assume hpq : p ∧ q,                   -- так, and.left == and.elim_left, а and.right == and.elim_right
    show q ∧ p, from ⟨and.right hpq, and.left hpq⟩ -- and.intro можно заменить на ⟨,⟩ (\< и \>)

  example : p ∧ q → q ∧ p :=
    assume hpq : p ∧ q,
    show q ∧ p, from ⟨hpq.right, hpq.left⟩-- еще немного сахара, and.left hpq можно заменить на hpq.left и т.д.

  example (hpq : p ∧ q) : q ∧ p :=        -- наиболее короткая, но, imho, малопонятная запись доказательства
  ⟨hpq.right, hpq.left⟩                   -- каждый выбирает сам, но, кажется, в доказательстве теорем решает вербозность
end stdlib_and

-- or

namespace stdlib_or
  variables p q : Prop

  example : p → p ∨ q :=                  -- аналогичные вещи есть для и ∨ (и кучи других вещей)
    assume hp : p,
    show p ∨ q, from or.intro_left q hp

  example : q → p ∨ q :=
    assume hq : q,
    show p ∨ q, from or.intro_right p hq

  example : p ∨ q → q ∨ p :=
    assume hpq : p ∨ q,
    or.elim hpq                           -- or.elim (x : a ∨ b) предлагат рассмотреть два случая: истинность а и b
      (assume hp : p,                     -- если в обоих случаях удается привести доказательство, то общее утверждение доказано
      show q ∨ p, from or.inr hp)
      (assume hq : q,                     -- or.inr и or.inl являются аналогами intro_*, но с обоими неявными аргументами
      show q ∨ p, from or.inl hq)

  #check @or.elim        -- ∀ {a b c : Prop}, a ∨ b → (a → c) → (b → c)
  #check @or.intro_left  -- ∀ (a : Prop) {b : Prop}, a → a ∨ b
  #check @or.intro_right -- ∀ {a : Prop} (b : Prop), b → a ∨ b
  #check @or.inl         -- ∀ {a b : Prop}, a → a ∨ b
  #check @or.inr         -- ∀ {a b : Prop}, b → a ∨ b
end stdlib_or

-- not

namespace stdlib_not
  variables p q : Prop

  example : (p → q) → ¬q → ¬p :=          -- not играет роль обычной унарной функции типа p → false
    assume h₁ : p → q,                    -- в связи с этим в assume последнего отрицания можно писать
    assume h₂ : ¬q,                       -- assume h : p, и тогда останется доказать только false
    assume h₃ : p,
    show false, from h₂ (h₁ h₃)

  example : p → ¬p → q :=                 -- false.elim позволяет вывести что угодно из лжи
    assume hp : p,
    assume hnp : ¬p,
    false.elim (hnp hp)

  example : p → ¬p → q :=                 -- аналогичным поведением обладает absurd, принимающий утверждение и его отрицание,
    assume hp : p,                        -- и возвращающий что угодно
    assume hnp : ¬p,
    absurd hp hnp

  #check @false.elim -- Π {c : Type u}, false → c
  #check @absurd     -- Π {a : Prop} {c : Type u}, a → ¬a → c
end stdlib_not

-- equiv

namespace stdlib_equiv
  variables p q : Prop

  theorem and_swap : p ∧ q ↔ q ∧ p :=
    iff.intro                             -- для введения эквивалентности требуется доказать импликацию в каждую сторону
      (assume hpq : p ∧ q,
      show q ∧ p, from and.swap hpq)
      (assume hqp : q ∧ p,
      show p ∧ q, from and.swap hqp)

  #check @iff.intro -- ∀ {a b : Prop}, (a → b) → (b → a) → (a ↔ b)
  #check @and.swap  -- ∀ {a b : Prop}, a ∧ b → b ∧ a

  #check and_swap p q  -- p ∧ q ↔ q ∧ p

  example (h : p ∧ q) : q ∧ p :=          -- очень полезным бывает получение импликации из эквиваленции: (a ↔ b) → a → b,
    iff.mp (and_swap p q) h               -- то есть применение эквиваленции в одну сторону; для этого есть две полезные
                                          -- функции: iff.mp и iff.mpr (modus ponens и modus ponens reverse)

  #check @iff.mp  -- ∀ {a b : Prop}, (a ↔ b) → a → b
  #check @iff.mpr -- ∀ {a b : Prop}, (a ↔ b) → b → a

end stdlib_equiv

-- classical

namespace stdlib_classical                -- несмотря на то, что правильная логика — интуиционистская,
  open classical                          -- мы можем пользоваться и классической, импортировав её из classical

  #check em -- ∀ {p : Prop}, p ∨ ¬p       -- основная аксиома, которая есть в классической логике и отсуствует в нормальной

  theorem dne {p : Prop} (h : ¬¬p) : p := -- докажем закон снятия двойного отрицания (double negation elimination)
    or.elim (em p)                        -- рассмотрим случаи истинности и ложности p с помощью уже знакомого or.elim
      (assume hp  : p,
       show p, from hp)                   -- если p истино, то все тривиально
      (assume hnp : ¬p,
       show p, from absurd hnp h)         -- иначе у нас есть и ¬p и его отрицание ¬(¬p), что дает нам что угодно

  example {p : Prop} (h : ¬¬p) : p :=     -- аналогичное доказательство можно построить, используя конструкцию by_cases,
    by_cases                              -- являющейся комбинацией or.elim и em
      (assume hp  : p,
       show p, from hp)
      (assume hnp : ¬p,
       show p, from absurd hnp h)

  #check @by_cases -- ∀ {p q : Prop}, (p → q) → (¬p → q) → q

  example {p : Prop} (h : ¬¬p) : p :=     -- еще один элигантный способ: доказательство от противного, где мы получаем отрицание того,
    by_contradiction                      -- что хотим доказать, и должны вывести false
      (assume hnp : ¬p,
       show false, from h hnp)

  #check @by_contradiction -- ∀ {p : Prop}, (¬p → false) → p

end stdlib_classical


/-
Copyright (c) 2021 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebraic_geometry.prime_spectrum
import Mathlib.ring_theory.polynomial.basic
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
The morphism `Spec R[x] --> Spec R` induced by the natural inclusion `R --> R[x]` is an open map.
-/

namespace algebraic_geometry

namespace polynomial

/-- Given a polynomial `f ∈ R[x]`, `image_of_Df` is the subset of `Spec R` where at least one
of the coefficients of `f` does not vanish.  Lemma `image_of_Df_eq_comap_C_compl_zero_locus`
proves that `image_of_Df` is the image of `(zero_locus {f})ᶜ` under the morphism
`comap C : Spec R[x] → Spec R`. -/
def image_of_Df {R : Type u_1} [comm_ring R] (f : polynomial R) : set (prime_spectrum R) :=
  set_of fun (p : prime_spectrum R) => ∃ (i : ℕ), ¬polynomial.coeff f i ∈ prime_spectrum.as_ideal p

theorem is_open_image_of_Df {R : Type u_1} [comm_ring R] {f : polynomial R} :
    is_open (image_of_Df f) :=
  sorry

/-- If a point of `Spec R[x]` is not contained in the vanishing set of `f`, then its image in
`Spec R` is contained in the open set where at least one of the coefficients of `f` is non-zero.
This lemma is a reformulation of `exists_coeff_not_mem_C_inverse`. -/
theorem comap_C_mem_image_of_Df {R : Type u_1} [comm_ring R] {f : polynomial R}
    {I : prime_spectrum (polynomial R)} (H : I ∈ (prime_spectrum.zero_locus (singleton f)ᶜ)) :
    prime_spectrum.comap polynomial.C I ∈ image_of_Df f :=
  polynomial.exists_coeff_not_mem_C_inverse
    (iff.mp prime_spectrum.mem_compl_zero_locus_iff_not_mem H)

/-- The open set `image_of_Df f` coincides with the image of `basic_open f` under the
morphism `C⁺ : Spec R[x] → Spec R`. -/
theorem image_of_Df_eq_comap_C_compl_zero_locus {R : Type u_1} [comm_ring R] {f : polynomial R} :
    image_of_Df f =
        prime_spectrum.comap polynomial.C '' (prime_spectrum.zero_locus (singleton f)ᶜ) :=
  sorry

/--  The morphism `C⁺ : Spec R[x] → Spec R` is open. -/
theorem is_open_map_comap_C {R : Type u_1} [comm_ring R] :
    is_open_map (prime_spectrum.comap polynomial.C) :=
  sorry

end Mathlib


import algebra.ring algebra.big_operators.basic
import data.birange

universes u v

variables (α : Type u) (β : Type v)

def power_series := ℕ → α

namespace power_series

@[ext]
lemma ext {a b : power_series α} : a = b ↔ ∀ k, (a k) = (b k) :=
 ⟨λ e, congr_fun e,λ e,funext e⟩

section add_comm_monoid

variable [add_comm_monoid α]
variables a b c : power_series α
variables i j k n m : ℕ

instance : add_comm_monoid (power_series α) :=
 by {unfold power_series, apply_instance}

variable {α}

def C (x : α) : power_series α
| 0 := x
| (n + 1) := 0

lemma C_coeff_zero {x : α} : (C x) 0 = x := rfl
lemma C_coeff_succ {x : α} (k : ℕ) : (C x) (k + 1) = (0 : α) := rfl
lemma C_coeff {x : α} : ∀ (k : ℕ), (C x) k = ite (k = 0) x (0 : α)
| 0 := by {rw[C_coeff_zero,if_pos rfl],}
| (k + 1) := by {rw[C_coeff_succ,if_neg (nat.succ_ne_zero k)],}

@[simp]
lemma zero_coeff : (0 : power_series α) k = 0 := rfl

@[simp]
lemma add_coeff : (a + b) k = (a k) + (b k) := rfl

lemma C_zero : C (0 : α) = 0 :=
 by {ext n, rw[zero_coeff],cases n,rw[C_coeff_zero],rw[C_coeff_succ]}

lemma C_add {x y : α} : C (x + y) = C x + C y :=
 by {ext n, rw[add_coeff],cases n,repeat{rw[C_coeff_zero]},
     repeat{rw[C_coeff_succ]},rw[add_zero]}

end add_comm_monoid

section semiring

variable [semiring α]

variable {α}
variables a b c : power_series α
variables x y z : α
variables i j k n m : ℕ

def one : (power_series α)
| 0 := 1
| (k + 1) := 0

instance : has_one (power_series α) := ⟨one⟩

lemma one_coeff_zero : (1 : power_series α) 0 = (1 : α) := rfl
lemma one_coeff_succ (k : ℕ) : (1 : power_series α) (k + 1) = (0 : α) := rfl
lemma one_coeff : ∀ (k : ℕ), (1 : power_series α) k = ite (k = 0) (1 : α) (0 : α)
| 0 := by {rw[one_coeff_zero,if_pos rfl],}
| (k + 1) := by {rw[one_coeff_succ,if_neg (nat.succ_ne_zero k)],}

lemma C_one : C (1 : α) = 1 :=
 by {ext n,cases n,rw[one_coeff_zero,C_coeff_zero],rw[one_coeff_succ,C_coeff_succ]}

def X : (power_series α)
| 0 := 0
| 1 := 1
| (k + 2) := 0

lemma X_coeff_zero      : (X : power_series α) 0       = 0 := rfl
lemma X_coeff_one       : (X : power_series α) 1       = 1 := rfl
lemma X_coeff_succ_succ : (X : power_series α) (k + 2) = 0 := rfl
lemma X_coeff : ∀ (k : ℕ), (X : power_series α) k = ite (k = 1) 1 0
| 0 := by {rw[X_coeff_zero, if_neg (dec_trivial : 0 ≠ 1)]}
| 1 := by {rw[X_coeff_one, if_pos rfl]}
| (k + 2) := by {have : k + 2 ≠ 1 := λ h, by {cases h},
                 rw[X_coeff_succ_succ,if_neg this],}

def shift : power_series α → power_series α := λ a k, (a (k + 1))

lemma shift_coeff : (shift a) k = a (k + 1) := rfl

lemma shift_zero : shift (0 : power_series α)  = 0 := by { ext, refl, }
lemma shift_one  : shift (1 : power_series α)  = 0 := by { ext, refl, }
lemma shift_C {x : α} : shift (C x) = 0 := by {ext, refl,}
lemma shift_add : shift (a + b) = shift a + shift b := by {ext, refl}

lemma shift_X : shift (X : power_series α) = 1 :=
 by { ext i, cases i with i,
     {rw[shift_coeff,one_coeff_zero,zero_add,X_coeff_one],},
     {rw[shift_coeff,one_coeff_succ,X_coeff_succ_succ]}}

def smul (x : α) (a : power_series α) : power_series α := λ k, x * (a k)
def rmul (a : power_series α) (x : α) : power_series α := λ k, (a k) * x

@[simp] lemma smul_coeff : smul x a k = x * (a k) := rfl
@[simp] lemma rmul_coeff : rmul a x k = (a k) * x := rfl

lemma shift_smul : shift (smul x a) = smul x (shift a) := by { ext, refl, }
lemma shift_rmul : shift (rmul a x) = rmul (shift a) x := by { ext, refl, }

lemma smul_C : smul x (C y) = C (x * y) :=
by { ext n, cases n, refl, dsimp[smul,C],rw[mul_zero],}

lemma C_smul : rmul (C x) y = C (x * y) :=
by { ext n, cases n, refl, dsimp[rmul,C],rw[zero_mul],}

lemma zero_smul : smul (0 : α) a = 0 := by { ext n, simp only[smul,zero_coeff,zero_mul],}
lemma rmul_zero : rmul a (0 : α) = 0 := by { ext n, simp only[rmul,zero_coeff,mul_zero],}
lemma smul_zero : smul x 0 = 0 :=  by { ext n, simp only[smul,zero_coeff,mul_zero],}
lemma zero_rmul : rmul 0 x = 0 :=  by { ext n, simp only[rmul,zero_coeff,zero_mul],}

lemma one_smul : smul (1 : α) a = a := by { ext n,rw[smul_coeff,one_mul],}
lemma rmul_one : rmul a (1 : α) = a := by { ext n,rw[rmul_coeff,mul_one],}

lemma add_smul : smul (x + y) a = (smul x a) + (smul y a) :=
 by {ext n, simp only[smul_coeff,add_coeff,add_mul],}
lemma rmul_add : rmul a (x + y) = (rmul a x) + (rmul a y) :=
 by {ext n, simp only[rmul_coeff,add_coeff,mul_add],}
lemma smul_add : smul x (a + b) = smul x a + smul x b :=
 by {ext n, simp only[smul_coeff,add_coeff,mul_add],}
lemma add_rmul : rmul (a + b) x = rmul a x + rmul b x :=
 by {ext n, simp only[rmul_coeff,add_coeff,add_mul],}

def mul : ∀ (a b : power_series α), power_series α
| a b 0 := (a 0) * (b 0)
| a b (n + 1) := (a (n + 1)) * (b 0) + (mul a b.shift) n

instance : has_mul (power_series α) := ⟨mul⟩

lemma mul_coeff_zero : (a * b) 0 = (a 0) * (b 0) := rfl

lemma mul_coeff_succ :
 (a * b) (n + 1) = (a (n + 1)) * (b 0) + (a * b.shift) n := rfl

lemma mul_coeff_succ' :
 (a * b) (n + 1) = (a 0) * (b (n + 1)) + (a.shift * b) n :=
begin
 induction n with n ih generalizing a b,
 {rw[mul_coeff_succ,mul_coeff_zero,mul_coeff_zero,shift_coeff,shift_coeff,zero_add,add_comm],},
 {rw[mul_coeff_succ,ih a b.shift,mul_coeff_succ,shift_coeff,shift_coeff],
  rw[← add_assoc,← add_assoc,add_comm ((a 0) * (b (n + 2)))],
 }
end

lemma mul_eq_sum :
 (a * b) n = (finset.range (n + 1)).sum (λ i, (a i) * (b (n - i))) :=
begin
 induction n with n ih generalizing a b,
 {rw[mul_coeff_zero,finset.sum_range_succ,finset.range_zero,finset.sum_empty,zero_add]},
 {rw[mul_coeff_succ,finset.sum_range_succ,nat.sub_self,ih a b.shift,add_comm],
  congr' 1,apply finset.sum_congr rfl,intros i hi,rw[shift_coeff],
  congr,
  exact (nat.succ_sub (nat.le_of_lt_succ (finset.mem_range.mp hi))).symm,
 }
end

lemma C_mul : (C x) * a = smul x a :=
 by {
  ext n, induction n with n ih generalizing a,
  {rw[mul_coeff_zero,C_coeff_zero,smul]},
  {rw[mul_coeff_succ,ih,C_coeff_succ,_root_.zero_mul (a 0),zero_add,← shift_smul,shift_coeff],}
 }

lemma zero_mul : (0 : power_series α) * a = 0 :=
 by { rw[← C_zero,C_mul,zero_smul,C_zero],}

lemma one_mul : (1 : power_series α) * a = a :=
 by { rw[← C_one,C_mul,one_smul],}

lemma mul_zero : a * (0 : power_series α) = 0 :=
 by {
  ext n, induction n with n ih generalizing a,
  {rw[mul_coeff_zero,zero_coeff,mul_zero]},
  {rw[mul_coeff_succ,shift_zero,ih,zero_coeff,zero_coeff,zero_coeff,mul_zero,zero_add],}
 }

lemma mul_C : a * (C x) = rmul a x :=
 by {
  ext n, cases n,
  {rw[mul_coeff_zero,C_coeff_zero,rmul]},
  {rw[mul_coeff_succ,C_coeff_zero,shift_C,mul_zero,zero_coeff,add_zero,rmul],}
 }

lemma mul_one : a * (1 : power_series α) = a :=
 by { rw[← C_one,mul_C,rmul_one] }

lemma add_mul : (a + b) * c = a * c + b * c :=
 by {
  ext n,induction n with n ih generalizing c,
  {simp only [mul_coeff_zero,add_coeff,_root_.add_mul],},
  {rw[add_coeff,mul_coeff_succ,mul_coeff_succ,mul_coeff_succ],
   rw[ih c.shift,add_coeff,add_coeff,_root_.add_mul],
   simp only[add_comm,add_left_comm],}
 }

lemma mul_add : a * (b + c) = a * b + a * c :=
 by {
  ext n,induction n with n ih generalizing b c,
  {simp only [mul_coeff_zero,add_coeff,_root_.mul_add],},
  {rw[add_coeff,mul_coeff_succ,mul_coeff_succ,mul_coeff_succ],
   rw[shift_add,ih b.shift c.shift,add_coeff,add_coeff,_root_.mul_add],
   simp only[add_comm,add_left_comm],}
 }

lemma shift_mul : shift (a * b) = smul (a 0) b.shift + (a.shift * b) :=
 by {
  ext n,rw[shift_coeff,mul_coeff_succ',add_coeff,smul_coeff,shift_coeff],
 }

lemma smul_mul : (smul x a) * b = smul x (a * b) :=
 by {
  ext n,induction n with n ih generalizing b,
  {simp only[mul_coeff_zero,smul_coeff,_root_.mul_assoc]},
  {simp only[mul_coeff_succ,ih,smul_coeff,_root_.mul_add,_root_.mul_assoc],}
 }

lemma mul_rmul : a * rmul b x = rmul (a * b) x :=
 by {
  ext n,induction n with n ih generalizing b,
  {simp only[mul_coeff_zero,rmul_coeff,_root_.mul_assoc]},
  {simp only[mul_coeff_succ,shift_rmul,ih,rmul_coeff,_root_.add_mul,_root_.mul_assoc],}
 }

lemma mul_assoc : a * b * c = a * (b * c) :=
by {
  ext n,
  induction n with n ih generalizing a b c,
  {repeat{rw[mul_coeff_zero]},rw[_root_.mul_assoc]},
  {rw[mul_coeff_succ',mul_coeff_succ',mul_coeff_succ',shift_mul],
   rw[add_mul,add_coeff,smul_mul,smul_coeff,ih,_root_.mul_add],
   rw[mul_coeff_zero,mul_assoc,add_assoc],
  }
}

variable (α)

instance : semiring (power_series α) := {
  one := (1),
  mul := (*),
  one_mul := one_mul,
  mul_one := mul_one,
  mul_assoc := mul_assoc,
  zero_mul := zero_mul,
  mul_zero := mul_zero,
  left_distrib := mul_add,
  right_distrib := add_mul,
  .. (power_series.add_comm_monoid α)
 }

end semiring

end power_series


/- Test cases for cooper, from John Harrison's Handbook of Practical Logic and Automated Reasoning. -/

import .main

set_option profiler true

/- Theorems -/

open tactic lia

example : ∃ (x : int), x < 1 :=
by cooper

example : ∀ (x : int), ∃ (y : int), y = x + 1 :=
by cooper_vm

example : ∀ (x : int), ∃ (y : int), (2 * y ≤ x ∧ x < 2 * (y + 1)) :=
by cooper_vm

example : ∀ (y : int), ((∃ (d : int), y = 2 * d) → (∃ (c : int), y = 1 * c)) :=
by cooper_vm

example : ∀ (x y z : int), (2 * x + 1 = 2 * y) → 129 < x + y + z :=
by cooper_vm

example : ∃ (x y : int), 5 * x + 3 * y = 1 :=
by cooper_vm

example : ∃ (w x y z : int), 2 * w + 3 * x + 4 * y + 5 * z = 1 :=
by cooper_vm

example : ∀ (x y : int), 6 * x = 5 * y → ∃ d, y = 3 * d :=
by cooper_vm

example : ∀ (x : int), (¬(∃ m, x = 2 * m) ∧ (∃ m, x = 3 * m + 1))
             ↔ ((∃ m, x = 12 * m + 1) ∨ (∃ m, x = 12 * m + 7)) :=
by cooper_vm

-- example : ∃ (l : int), ∀ (x : int),
--   x ≥ l → ∃ (u v : int), u ≥ 0 ∧ v ≥ 0 ∧ x = 3 * u + 5 * v :=
-- by cooper_vm -- timeout

/- Nontheorems -/

--example : ∃ (x y z : int), 4 * x - 6 * y = 1 :=
--by cooper_vm

-- example : ∀ (x y : int), x ≤ y → ((2 * x) + 1) < 2 * y :=
-- by cooper_vm

-- example : ∀ (a b : int), ∃ (x : int), a < 20 * x /\ 20 * x < b :=
-- by cooper_vm

-- example : ∃ (y : int), ∀ x, 2 ≤ x + 5 * y ∧ 2 ≤ 13 * x - y ∧ x + 3 ≤ 0 :=
-- by cooper_vm

-- example : ∀ (x y : int), ¬(x = 0) → 5 * y + 1 ≤ 6 * x ∨ 6 * x + 1 ≤ 5 * y  :=
-- by cooper_vm

--  example : ∀ (z : int), 3 ≤ z → ∃ (x y : int), x ≥ 0 ∧ y ≥ 0 ∧ 3 * x + 5 * y = z :=
--  by cooper_vm -- timeout

-- example : ∃ (a b : int), a ≥ 2 ∧ b ≥ 2 ∧ ((2 * b = a) ∨ (2 * b = 3 * a + 1)) ∧ (a = b) :=
-- by cooper_vm


import tactic
import data.set data.set.finite data.finset data.fintype.basic

structure fin_simplicial_complex := mk ::
(vertices : Type*)
(de : decidable_eq vertices := by apply_instance)
(ft : fintype vertices := by apply_instance)
(simplices : finset (finset vertices))
(nonempty : ∀ {σ : (finset vertices)}, σ ∈ simplices → finset.nonempty σ)
(singleton : ∀ v : vertices, {v} ∈ simplices)
(downwards : ∀ {σ τ : (finset vertices)}, σ ∈ simplices → τ ⊆ σ → τ.nonempty → τ ∈ simplices)

namespace fin_simplicial_complex

variable {K : fin_simplicial_complex}

instance : decidable_eq K.vertices := K.de
instance : fintype K.vertices := K.ft

lemma nonempty' : ¬ ((∅ : finset K.vertices) ∈ K.simplices) := λ h,
  finset.not_nonempty_empty (K.nonempty h)

def singleton_simplex (v : K.vertices) : K.simplices :=
  ⟨{v}, K.singleton v⟩

def empty : fin_simplicial_complex := {
  vertices := empty,
  de := by apply_instance,
  ft := by apply_instance,
  simplices := ∅,
  nonempty := λ σ h, (set.not_mem_empty σ h).elim,
  singleton := λ v, empty.elim v,
  downwards := λ σ τ hσ hτσ hτ, (set.not_mem_empty σ hσ).elim
}

def singleton_emb (V : Type*) : V ↪ finset V := {
  to_fun := λ v, {v},
  inj' := λ a b h, finset.singleton_inj.mp h,
}

#check tactic.hint
def discrete (V : Type*) [decidable_eq V] [fintype V] : fin_simplicial_complex := {
  vertices := V,
  de := by apply_instance,
  ft := by apply_instance,
  simplices := (finset.univ : finset V).map (singleton_emb V),
  nonempty := λ σ h, begin
    rw[finset.mem_map] at h, rcases h with ⟨v, v_in_univ, rfl⟩,
    use v, exact finset.mem_singleton_self v
  end,
  singleton := λ v, begin rw[finset.mem_map], exact ⟨v, finset.mem_univ v,rfl⟩ end,
  downwards := λ σ τ hσ hτσ hτ, begin
    rw[finset.mem_map] at hσ ⊢,
    rcases hσ with ⟨v, v_in_univ, rfl⟩,
    rcases finset.subset_singleton_iff.mp hτσ with (rfl|rfl),
    { exfalso, exact finset.not_nonempty_empty hτ },
    { exact ⟨v, finset.mem_univ v, rfl⟩ }
  end
}

def indiscrete (V : Type*) [decidable_eq V] [fintype V] : fin_simplicial_complex := {
  vertices := V,
  de := by apply_instance,
  ft := by apply_instance,
  simplices := finset.univ.filter (λ σ, σ ≠ ∅),
  nonempty := λ σ h, by { rw[finset.mem_filter] at h, exact finset.nonempty_of_ne_empty h.2 },
  singleton := λ v, by { rw[finset.mem_filter, ← finset.nonempty_iff_ne_empty],
    exact ⟨finset.mem_univ {v}, finset.singleton_nonempty v⟩
  },
  downwards := λ σ τ hσ hτσ hτ, by {
    rw[finset.mem_filter], rw[finset.nonempty_iff_ne_empty] at hτ,
    exact ⟨finset.mem_univ τ, hτ⟩
  }
}

def indiscrete_simplex {V : Type*} [decidable_eq V] [fintype V]
   {σ : finset V} (h : σ ≠ ∅) : simplices (indiscrete V) :=
⟨σ, by { dsimp[indiscrete], rw[finset.mem_filter], exact ⟨finset.mem_univ σ, h⟩ }⟩

def standard (n : ℕ) : fin_simplicial_complex := indiscrete (fin n.succ)

def standard_simplex {n : ℕ}
   {σ : finset (fin n.succ)} (h : σ ≠ ∅ ): simplices (standard n) :=
  indiscrete_simplex h

def top_standard_simplex (n : ℕ) :=
  @standard_simplex n finset.univ (λ h, by {
    have h' := finset.mem_univ (0 : fin n.succ),
    rw[h] at h', exact finset.not_mem_empty _ h'
  })

def sphere (n : ℕ) : fin_simplicial_complex := {
  vertices := fin (n + 2),
  de := by apply_instance,
  ft := by apply_instance,
  simplices := finset.univ.filter (λ σ, σ ≠ ∅ ∧ σ ≠ finset.univ),
  nonempty := λ σ h, by { rw[finset.mem_filter] at h, exact finset.nonempty_of_ne_empty h.2.1 },
  singleton := λ v, by { rw[finset.mem_filter, ← finset.nonempty_iff_ne_empty],
    split, exact finset.mem_univ {v},
    split, exact finset.singleton_nonempty v,
    intro h,
    by_cases h' : v = 0,
    { have := finset.mem_univ (1 : fin (n + 2)),
      rw[← h, h', finset.mem_singleton] at this,
      cases this },
    { have := finset.mem_univ (0 : fin (n + 2)),
      rw[← h, finset.mem_singleton] at this,
      exact h' (eq.symm this)
    }
  },
  downwards := λ σ τ hσ hτσ hτ, by {
    rw[finset.mem_filter] at hσ ⊢, rw[finset.nonempty_iff_ne_empty] at hτ,
    split, exact finset.mem_univ τ,
    split, exact hτ,
    rintro rfl, apply hσ.2.2,
    exact subset_antisymm (finset.subset_univ σ) hτσ
  }
}

def of_generators {V : Type*} [decidable_eq V] [fintype V]
  (S : finset (finset V)) : fin_simplicial_complex := {
  vertices := V,
  de := by apply_instance,
  ft := by apply_instance,
  simplices := finset.univ.filter (λ σ, σ ≠ ∅ ∧
    (σ.card ≤ 1 ∨ ∃ τ, τ ∈ S ∧ σ ⊆ τ)
  ),
  nonempty := λ σ h, by {
    rw[finset.mem_filter] at h,
    exact finset.nonempty_of_ne_empty h.2.1
  },
  singleton := λ v, by {
    rw[finset.mem_filter],
    split, exact finset.mem_univ {v},
    split, rw[← finset.nonempty_iff_ne_empty], exact finset.singleton_nonempty v,
    left, exact le_refl _
  },
  downwards := λ σ τ hσ hτσ hτ, by {
    rw[finset.mem_filter] at hσ ⊢,
    split, exact finset.mem_univ τ,
    split, rw[← finset.nonempty_iff_ne_empty], exact hτ,
    rcases hσ with ⟨hu,hn,hc|⟨ρ,hρS,hσρ⟩⟩,
    { left, exact le_trans (finset.card_le_of_subset hτσ) hc },
    { right, exact ⟨ρ, hρS, finset.subset.trans hτσ hσρ⟩}
  }
}

def of_fin_generators (n : ℕ) (S : finset (finset (fin n))) : fin_simplicial_complex :=
  of_generators S

def dim (σ : K.simplices) : ℕ := (σ : finset K.vertices).card.pred

lemma simplex_card (σ : K.simplices) :
   (σ : finset K.vertices).card = fin_simplicial_complex.dim σ + 1 :=
begin
  rcases σ with ⟨σ, hσ⟩,
  symmetry,
  apply nat.succ_pred_eq_of_pos, apply nat.pos_of_ne_zero,
  change σ.card ≠ 0,
  intro h,
  rw[finset.card_eq_zero.mp h] at hσ,
  exact fin_simplicial_complex.nonempty' hσ
end

@[derive decidable]
def subdim (K : fin_simplicial_complex) (n : ℕ) :=
  ∀ (σ : K.simplices), fin_simplicial_complex.dim σ ≤ n

@[derive decidable]
def supdim (K : fin_simplicial_complex) (n : ℕ) :=
  ∃ (σ : K.simplices), fin_simplicial_complex.dim σ ≥ n

@[derive decidable]
def dimeq (K : fin_simplicial_complex) (n : ℕ) :=
  subdim K n ∧ supdim K n

lemma dim_standard (n : ℕ) : dimeq (standard n) n :=
begin
  split,
  { rintro ⟨σ : finset (fin n.succ),hσ⟩, change σ.card.pred ≤ n,
    have := finset.card_le_univ σ,
    rw[fintype.card_fin, ← nat.pred_le_iff] at this,
    exact this
  }, {
    use top_standard_simplex n,
    change finset.univ.card.pred ≥ n,
    rw[finset.card_univ],
    change (fintype.card (fin n.succ)).pred ≥ n,
    rw[fintype.card_fin, nat.pred_succ],
    exact le_refl n
  }
end

structure hom (K L : fin_simplicial_complex) := mk ::
(to_fun : K.vertices → L.vertices)
(map_simplex : ∀ {σ : finset K.vertices} (h : σ ∈ K.simplices), σ.image to_fun ∈ L.simplices)

namespace hom

def to_fun' {K L : fin_simplicial_complex} (f : hom K L) : K.simplices → L.simplices :=
  λ σ, ⟨(σ : finset K.vertices).image f.to_fun, f.map_simplex σ.property⟩

def id (K : fin_simplicial_complex) : hom K K := {
  to_fun := id,
  map_simplex := λ σ h, by { rw[finset.image_id], exact h }
}

lemma id' (K : fin_simplicial_complex) : (id K).to_fun' = (_root_.id : K.simplices → K.simplices) :=
begin
  funext σ, rcases σ with ⟨σ,h⟩, ext1,
  change σ.image _root_.id = σ, rw[finset.image_id]
end

def comp {K L M : fin_simplicial_complex} (g : hom L M) (f : hom K L) : hom K M := {
  to_fun := g.to_fun ∘ f.to_fun,
  map_simplex := λ σ h,
  begin
    rw[← finset.image_image],
    exact g.map_simplex (f.map_simplex h)
  end
}

def comp' {K L M : fin_simplicial_complex} (g : hom L M) (f : hom K L) :
  (comp g f).to_fun' = g.to_fun' ∘ f.to_fun' :=
begin
  funext σ, rcases σ with ⟨σ,h⟩, ext1,
  rw[function.comp],
  change σ.image (g.to_fun ∘ f.to_fun) = (σ.image f.to_fun).image g.to_fun,
  rw[finset.image_image]
end

def const (K : fin_simplicial_complex) {L : fin_simplicial_complex} (w : L.vertices) : hom K L := {
  to_fun := function.const K.vertices w,
  map_simplex := λ σ h, by {
    rw[finset.image_const (K.nonempty h) w], exact L.singleton w
  }
}

lemma const' (K : fin_simplicial_complex) {L : fin_simplicial_complex} (w : L.vertices) :
  (const K w).to_fun' = function.const K.simplices (fin_simplicial_complex.singleton_simplex w) :=
begin
  funext σ, rcases σ with ⟨σ, h⟩, ext1,
  change σ.image (function.const _ w) = {w},
  rw[finset.image_const (K.nonempty h) w]
end

end hom

namespace examples

def cylinder := of_fin_generators 6 {
  {0,1,4},{0,3,4},{1,2,5},{1,4,5},{2,3,0},{2,5,3}
}

def mobius_strip := of_fin_generators 6 {
  {0,1,4},{0,3,4},{1,2,5},{1,4,5},{2,3,0},{2,5,0}
}

def octahedron := of_fin_generators 6 {
  {0,1,4},{0,1,5},{0,3,4},{0,3,5},{1,2,4},{1,2,5},{2,3,4},{2,3,5}
}

def torus := of_fin_generators 9 {
  {0, 1, 4}, {0, 3, 4}, {1, 2, 5}, {1, 4, 5}, {2, 0, 3}, {2, 5, 3},
  {3, 4, 7}, {3, 6, 7}, {4, 5, 8}, {4, 7, 8}, {5, 3, 6}, {5, 8, 6},
  {6, 7, 1}, {6, 0, 1}, {7, 8, 2}, {7, 1, 2}, {8, 6, 0}, {8, 2, 0}
}

def klein_bottle := of_fin_generators 9 {
  {0, 1, 4}, {0, 3, 4}, {1, 2, 5}, {1, 4, 5}, {2, 0, 6}, {2, 5, 6},
  {3, 4, 7}, {3, 6, 7}, {4, 5, 8}, {4, 7, 8}, {5, 3, 6}, {5, 8, 3},
  {6, 7, 1}, {6, 0, 1}, {7, 8, 2}, {7, 1, 2}, {8, 3, 0}, {8, 2, 0}
}

def projective_plane  := of_fin_generators 9 {
  {0, 1, 5}, {0, 4, 5}, {1, 2, 6}, {1, 5, 6}, {2, 3, 6}, {3, 6, 7},
  {4, 5, 8}, {4, 7, 8}, {5, 6, 9}, {5, 8, 9}, {6, 7, 4}, {6, 4, 9},
  {7, 8, 2}, {7, 3, 2}, {8, 9, 1}, {8, 2, 1}, {9, 4, 0}, {9, 1, 0}
}

end examples
end fin_simplicial_complex


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

! This file was ported from Lean 3 source module data.set.mul_antidiagonal
! leanprover-community/mathlib commit 0a0ec35061ed9960bf0e7ffb0335f44447b58977
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Order.WellFoundedSet

/-! # Multiplication antidiagonal -/

namespace Set

variable {α : Type _}

section Mul

variable [Mul α] {s s₁ s₂ t t₁ t₂ : Set α} {a : α} {x : α × α}

/-- `Set.mulAntidiagonal s t a` is the set of all pairs of an element in `s` and an element in `t`
that multiply to `a`. -/
@[to_additive
      "`Set.addAntidiagonal s t a` is the set of all pairs of an element in `s` and an
      element in `t` that add to `a`."]
def mulAntidiagonal (s t : Set α) (a : α) : Set (α × α) :=
  { x | x.1 ∈ s ∧ x.2 ∈ t ∧ x.1 * x.2 = a }
#align set.mul_antidiagonal Set.mulAntidiagonal
#align set.add_antidiagonal Set.addAntidiagonal

@[to_additive (attr := simp)]
theorem mem_mulAntidiagonal : x ∈ mulAntidiagonal s t a ↔ x.1 ∈ s ∧ x.2 ∈ t ∧ x.1 * x.2 = a :=
  Iff.rfl
#align set.mem_mul_antidiagonal Set.mem_mulAntidiagonal
#align set.mem_add_antidiagonal Set.mem_addAntidiagonal

@[to_additive]
theorem mulAntidiagonal_mono_left (h : s₁ ⊆ s₂) : mulAntidiagonal s₁ t a ⊆ mulAntidiagonal s₂ t a :=
  fun _ hx => ⟨h hx.1, hx.2.1, hx.2.2⟩
#align set.mul_antidiagonal_mono_left Set.mulAntidiagonal_mono_left
#align set.add_antidiagonal_mono_left Set.addAntidiagonal_mono_left

@[to_additive]
theorem mulAntidiagonal_mono_right (h : t₁ ⊆ t₂) :
    mulAntidiagonal s t₁ a ⊆ mulAntidiagonal s t₂ a := fun _ hx => ⟨hx.1, h hx.2.1, hx.2.2⟩
#align set.mul_antidiagonal_mono_right Set.mulAntidiagonal_mono_right
#align set.add_antidiagonal_mono_right Set.addAntidiagonal_mono_right

end Mul

-- Porting note: Removed simp attribute, simpnf linter can simplify lhs. Added aux version below
@[to_additive]
theorem swap_mem_mulAntidiagonal [CommSemigroup α] {s t : Set α} {a : α} {x : α × α} :
    x.swap ∈ Set.mulAntidiagonal s t a ↔ x ∈ Set.mulAntidiagonal t s a := by
  simp [mul_comm, and_left_comm]
#align set.swap_mem_mul_antidiagonal Set.swap_mem_mulAntidiagonal
#align set.swap_mem_add_antidiagonal Set.swap_mem_addAntidiagonal

@[to_additive (attr := simp)]
theorem swap_mem_mulAntidiagonal_aux [CommSemigroup α] {s t : Set α} {a : α} {x : α × α} :
     x.snd ∈ s ∧ x.fst ∈ t ∧ x.snd * x.fst = a
      ↔ x ∈ Set.mulAntidiagonal t s a := by
  simp [mul_comm, and_left_comm]

namespace MulAntidiagonal

section CancelCommMonoid

variable [CancelCommMonoid α] {s t : Set α} {a : α} {x y : mulAntidiagonal s t a}

-- Porting note: to_additive cannot translate the "Mul" in "MulAntidiagonal" by itself here
@[to_additive Set.AddAntidiagonal.fst_eq_fst_iff_snd_eq_snd]
theorem fst_eq_fst_iff_snd_eq_snd : (x : α × α).1 = (y : α × α).1 ↔ (x : α × α).2 = (y : α × α).2 :=
  ⟨fun h =>
    mul_left_cancel
      (y.2.2.2.trans <| by
          rw [← h]
          exact x.2.2.2.symm).symm,
    fun h =>
    mul_right_cancel
      (y.2.2.2.trans <| by
          rw [← h]
          exact x.2.2.2.symm).symm⟩
#align set.mul_antidiagonal.fst_eq_fst_iff_snd_eq_snd Set.MulAntidiagonal.fst_eq_fst_iff_snd_eq_snd
#align set.add_antidiagonal.fst_eq_fst_iff_snd_eq_snd Set.AddAntidiagonal.fst_eq_fst_iff_snd_eq_snd

@[to_additive Set.AddAntidiagonal.eq_of_fst_eq_fst]

@[to_additive Set.AddAntidiagonal.eq_of_snd_eq_snd]
theorem eq_of_snd_eq_snd (h : (x : α × α).snd = (y : α × α).snd) : x = y :=
  Subtype.ext <| Prod.ext (fst_eq_fst_iff_snd_eq_snd.2 h) h
#align set.mul_antidiagonal.eq_of_snd_eq_snd Set.MulAntidiagonal.eq_of_snd_eq_snd
#align set.add_antidiagonal.eq_of_snd_eq_snd Set.AddAntidiagonal.eq_of_snd_eq_snd

end CancelCommMonoid

section OrderedCancelCommMonoid

variable [OrderedCancelCommMonoid α] (s t : Set α) (a : α) {x y : mulAntidiagonal s t a}

@[to_additive Set.AddAntidiagonal.eq_of_fst_le_fst_of_snd_le_snd]
theorem eq_of_fst_le_fst_of_snd_le_snd (h₁ : (x : α × α).1 ≤ (y : α × α).1)
    (h₂ : (x : α × α).2 ≤ (y : α × α).2) : x = y :=
  eq_of_fst_eq_fst <|
    h₁.eq_of_not_lt fun hlt =>
      (mul_lt_mul_of_lt_of_le hlt h₂).ne <|
        (mem_mulAntidiagonal.1 x.2).2.2.trans (mem_mulAntidiagonal.1 y.2).2.2.symm
#align set.mul_antidiagonal.eq_of_fst_le_fst_of_snd_le_snd Set.MulAntidiagonal.eq_of_fst_le_fst_of_snd_le_snd
#align set.add_antidiagonal.eq_of_fst_le_fst_of_snd_le_snd Set.AddAntidiagonal.eq_of_fst_le_fst_of_snd_le_snd

variable {s t}

@[to_additive Set.AddAntidiagonal.finite_of_isPwo]
theorem finite_of_isPwo (hs : s.IsPwo) (ht : t.IsPwo) (a) : (mulAntidiagonal s t a).Finite := by
  refine' not_infinite.1 fun h => _
  have h1 : (mulAntidiagonal s t a).PartiallyWellOrderedOn (Prod.fst ⁻¹'o (· ≤ ·)) := fun f hf =>
    hs (Prod.fst ∘ f) fun n => (mem_mulAntidiagonal.1 (hf n)).1
  have h2 : (mulAntidiagonal s t a).PartiallyWellOrderedOn (Prod.snd ⁻¹'o (· ≤ ·)) := fun f hf =>
    ht (Prod.snd ∘ f) fun n => (mem_mulAntidiagonal.1 (hf n)).2.1
  obtain ⟨g, hg⟩ :=
    h1.exists_monotone_subseq (fun n => h.natEmbedding _ n) fun n => (h.natEmbedding _ n).2
  obtain ⟨m, n, mn, h2'⟩ := h2 (fun x => (h.natEmbedding _) (g x)) fun n => (h.natEmbedding _ _).2
  refine' mn.ne (g.injective <| (h.natEmbedding _).injective _)
  exact eq_of_fst_le_fst_of_snd_le_snd _ _ _ (hg _ _ mn.le) h2'
#align set.mul_antidiagonal.finite_of_is_pwo Set.MulAntidiagonal.finite_of_isPwo
#align set.add_antidiagonal.finite_of_is_pwo Set.AddAntidiagonal.finite_of_isPwo

end OrderedCancelCommMonoid

@[to_additive Set.AddAntidiagonal.finite_of_isWf]
theorem finite_of_isWf [LinearOrderedCancelCommMonoid α] {s t : Set α} (hs : s.IsWf) (ht : t.IsWf)
    (a) : (mulAntidiagonal s t a).Finite :=
  finite_of_isPwo hs.isPwo ht.isPwo a
#align set.mul_antidiagonal.finite_of_is_wf Set.MulAntidiagonal.finite_of_isWf
#align set.add_antidiagonal.finite_of_is_wf Set.AddAntidiagonal.finite_of_isWf

end MulAntidiagonal

end Set


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

! This file was ported from Lean 3 source module data.set.intervals.unordered_interval
! leanprover-community/mathlib commit 9003f28797c0664a49e4179487267c494477d853
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.Bounds.Basic
import Mathbin.Data.Set.Intervals.Basic

/-!
# Intervals without endpoints ordering

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

In any lattice `α`, we define `uIcc a b` to be `Icc (a ⊓ b) (a ⊔ b)`, which in a linear order is the
set of elements lying between `a` and `b`.

`Icc a b` requires the assumption `a ≤ b` to be meaningful, which is sometimes inconvenient. The
interval as defined in this file is always the set of things lying between `a` and `b`, regardless
of the relative order of `a` and `b`.

For real numbers, `uIcc a b` is the same as `segment ℝ a b`.

In a product or pi type, `uIcc a b` is the smallest box containing `a` and `b`. For example,
`uIcc (1, -1) (-1, 1) = Icc (-1, -1) (1, 1)` is the square of vertices `(1, -1)`, `(-1, -1)`,
`(-1, 1)`, `(1, 1)`.

In `finset α` (seen as a hypercube of dimension `fintype.card α`), `uIcc a b` is the smallest
subcube containing both `a` and `b`.

## Notation

We use the localized notation `[a, b]` for `uIcc a b`. One can open the locale `interval` to
make the notation available.

-/

open Function

open OrderDual (toDual ofDual)

variable {α β : Type _}

namespace Set

section Lattice

variable [Lattice α] {a a₁ a₂ b b₁ b₂ c x : α}

#print Set.uIcc /-
/-- `uIcc a b` is the set of elements lying between `a` and `b`, with `a` and `b` included.
Note that we define it more generally in a lattice as `set.Icc (a ⊓ b) (a ⊔ b)`. In a product type,
`uIcc` corresponds to the bounding box of the two elements. -/
def uIcc (a b : α) : Set α :=
  Icc (a ⊓ b) (a ⊔ b)
#align set.uIcc Set.uIcc
-/

-- mathport name: set.uIcc
scoped[Interval] notation "[" a ", " b "]" => Set.uIcc a b

#print Set.dual_uIcc /-
@[simp]
theorem dual_uIcc (a b : α) : [toDual a, toDual b] = ofDual ⁻¹' [a, b] :=
  dual_Icc
#align set.dual_uIcc Set.dual_uIcc
-/

#print Set.uIcc_of_le /-
@[simp]
theorem uIcc_of_le (h : a ≤ b) : [a, b] = Icc a b := by rw [uIcc, inf_eq_left.2 h, sup_eq_right.2 h]
#align set.uIcc_of_le Set.uIcc_of_le
-/

#print Set.uIcc_of_ge /-
@[simp]
theorem uIcc_of_ge (h : b ≤ a) : [a, b] = Icc b a := by rw [uIcc, inf_eq_right.2 h, sup_eq_left.2 h]
#align set.uIcc_of_ge Set.uIcc_of_ge
-/

#print Set.uIcc_comm /-
theorem uIcc_comm (a b : α) : [a, b] = [b, a] := by simp_rw [uIcc, inf_comm, sup_comm]
#align set.uIcc_comm Set.uIcc_comm
-/

#print Set.uIcc_of_lt /-
theorem uIcc_of_lt (h : a < b) : [a, b] = Icc a b :=
  uIcc_of_le h.le
#align set.uIcc_of_lt Set.uIcc_of_lt
-/

#print Set.uIcc_of_gt /-
theorem uIcc_of_gt (h : b < a) : [a, b] = Icc b a :=
  uIcc_of_ge h.le
#align set.uIcc_of_gt Set.uIcc_of_gt
-/

#print Set.uIcc_self /-
@[simp]
theorem uIcc_self : [a, a] = {a} := by simp [uIcc]
#align set.uIcc_self Set.uIcc_self
-/

#print Set.nonempty_uIcc /-
@[simp]
theorem nonempty_uIcc : [a, b].Nonempty :=
  nonempty_Icc.2 inf_le_sup
#align set.nonempty_uIcc Set.nonempty_uIcc
-/

#print Set.Icc_subset_uIcc /-
theorem Icc_subset_uIcc : Icc a b ⊆ [a, b] :=
  Icc_subset_Icc inf_le_left le_sup_right
#align set.Icc_subset_uIcc Set.Icc_subset_uIcc
-/

#print Set.Icc_subset_uIcc' /-
theorem Icc_subset_uIcc' : Icc b a ⊆ [a, b] :=
  Icc_subset_Icc inf_le_right le_sup_left
#align set.Icc_subset_uIcc' Set.Icc_subset_uIcc'
-/

#print Set.left_mem_uIcc /-
@[simp]
theorem left_mem_uIcc : a ∈ [a, b] :=
  ⟨inf_le_left, le_sup_left⟩
#align set.left_mem_uIcc Set.left_mem_uIcc
-/

#print Set.right_mem_uIcc /-
@[simp]
theorem right_mem_uIcc : b ∈ [a, b] :=
  ⟨inf_le_right, le_sup_right⟩
#align set.right_mem_uIcc Set.right_mem_uIcc
-/

#print Set.mem_uIcc_of_le /-
theorem mem_uIcc_of_le (ha : a ≤ x) (hb : x ≤ b) : x ∈ [a, b] :=
  Icc_subset_uIcc ⟨ha, hb⟩
#align set.mem_uIcc_of_le Set.mem_uIcc_of_le
-/

#print Set.mem_uIcc_of_ge /-
theorem mem_uIcc_of_ge (hb : b ≤ x) (ha : x ≤ a) : x ∈ [a, b] :=
  Icc_subset_uIcc' ⟨hb, ha⟩
#align set.mem_uIcc_of_ge Set.mem_uIcc_of_ge
-/

#print Set.uIcc_subset_uIcc /-
theorem uIcc_subset_uIcc (h₁ : a₁ ∈ [a₂, b₂]) (h₂ : b₁ ∈ [a₂, b₂]) : [a₁, b₁] ⊆ [a₂, b₂] :=
  Icc_subset_Icc (le_inf h₁.1 h₂.1) (sup_le h₁.2 h₂.2)
#align set.uIcc_subset_uIcc Set.uIcc_subset_uIcc
-/

#print Set.uIcc_subset_Icc /-
theorem uIcc_subset_Icc (ha : a₁ ∈ Icc a₂ b₂) (hb : b₁ ∈ Icc a₂ b₂) : [a₁, b₁] ⊆ Icc a₂ b₂ :=
  Icc_subset_Icc (le_inf ha.1 hb.1) (sup_le ha.2 hb.2)
#align set.uIcc_subset_Icc Set.uIcc_subset_Icc
-/

#print Set.uIcc_subset_uIcc_iff_mem /-
theorem uIcc_subset_uIcc_iff_mem : [a₁, b₁] ⊆ [a₂, b₂] ↔ a₁ ∈ [a₂, b₂] ∧ b₁ ∈ [a₂, b₂] :=
  Iff.intro (fun h => ⟨h left_mem_uIcc, h right_mem_uIcc⟩) fun h => uIcc_subset_uIcc h.1 h.2
#align set.uIcc_subset_uIcc_iff_mem Set.uIcc_subset_uIcc_iff_mem
-/

/- warning: set.uIcc_subset_uIcc_iff_le' -> Set.uIcc_subset_uIcc_iff_le' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Lattice.{u1} α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.uIcc.{u1} α _inst_1 a₁ b₁) (Set.uIcc.{u1} α _inst_1 a₂ b₂)) (And (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α _inst_1)))) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α _inst_1)) a₂ b₂) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α _inst_1)) a₁ b₁)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α _inst_1)))) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α _inst_1)) a₁ b₁) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α _inst_1)) a₂ b₂)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Lattice.{u1} α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.uIcc.{u1} α _inst_1 a₁ b₁) (Set.uIcc.{u1} α _inst_1 a₂ b₂)) (And (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α _inst_1)))) (Inf.inf.{u1} α (Lattice.toInf.{u1} α _inst_1) a₂ b₂) (Inf.inf.{u1} α (Lattice.toInf.{u1} α _inst_1) a₁ b₁)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α _inst_1)))) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α _inst_1)) a₁ b₁) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α _inst_1)) a₂ b₂)))
Case conversion may be inaccurate. Consider using '#align set.uIcc_subset_uIcc_iff_le' Set.uIcc_subset_uIcc_iff_le'ₓ'. -/
theorem uIcc_subset_uIcc_iff_le' : [a₁, b₁] ⊆ [a₂, b₂] ↔ a₂ ⊓ b₂ ≤ a₁ ⊓ b₁ ∧ a₁ ⊔ b₁ ≤ a₂ ⊔ b₂ :=
  Icc_subset_Icc_iff inf_le_sup
#align set.uIcc_subset_uIcc_iff_le' Set.uIcc_subset_uIcc_iff_le'

#print Set.uIcc_subset_uIcc_right /-
theorem uIcc_subset_uIcc_right (h : x ∈ [a, b]) : [x, b] ⊆ [a, b] :=
  uIcc_subset_uIcc h right_mem_uIcc
#align set.uIcc_subset_uIcc_right Set.uIcc_subset_uIcc_right
-/

#print Set.uIcc_subset_uIcc_left /-
theorem uIcc_subset_uIcc_left (h : x ∈ [a, b]) : [a, x] ⊆ [a, b] :=
  uIcc_subset_uIcc left_mem_uIcc h
#align set.uIcc_subset_uIcc_left Set.uIcc_subset_uIcc_left
-/

#print Set.bdd_below_bdd_above_iff_subset_uIcc /-
theorem bdd_below_bdd_above_iff_subset_uIcc (s : Set α) :
    BddBelow s ∧ BddAbove s ↔ ∃ a b, s ⊆ [a, b] :=
  bddBelow_bddAbove_iff_subset_Icc.trans
    ⟨fun ⟨a, b, h⟩ => ⟨a, b, fun x hx => Icc_subset_uIcc (h hx)⟩, fun ⟨a, b, h⟩ => ⟨_, _, h⟩⟩
#align set.bdd_below_bdd_above_iff_subset_uIcc Set.bdd_below_bdd_above_iff_subset_uIcc
-/

end Lattice

open Interval

section DistribLattice

variable [DistribLattice α] {a a₁ a₂ b b₁ b₂ c x : α}

#print Set.eq_of_mem_uIcc_of_mem_uIcc /-
theorem eq_of_mem_uIcc_of_mem_uIcc (ha : a ∈ [b, c]) (hb : b ∈ [a, c]) : a = b :=
  eq_of_inf_eq_sup_eq (inf_congr_right ha.1 hb.1) <| sup_congr_right ha.2 hb.2
#align set.eq_of_mem_uIcc_of_mem_uIcc Set.eq_of_mem_uIcc_of_mem_uIcc
-/

#print Set.eq_of_mem_uIcc_of_mem_uIcc' /-
theorem eq_of_mem_uIcc_of_mem_uIcc' : b ∈ [a, c] → c ∈ [a, b] → b = c := by
  simpa only [uIcc_comm a] using eq_of_mem_uIcc_of_mem_uIcc
#align set.eq_of_mem_uIcc_of_mem_uIcc' Set.eq_of_mem_uIcc_of_mem_uIcc'
-/

#print Set.uIcc_injective_right /-
theorem uIcc_injective_right (a : α) : Injective fun b => uIcc b a := fun b c h =>
  by
  rw [ext_iff] at h
  exact eq_of_mem_uIcc_of_mem_uIcc ((h _).1 left_mem_uIcc) ((h _).2 left_mem_uIcc)
#align set.uIcc_injective_right Set.uIcc_injective_right
-/

#print Set.uIcc_injective_left /-
theorem uIcc_injective_left (a : α) : Injective (uIcc a) := by
  simpa only [uIcc_comm] using uIcc_injective_right a
#align set.uIcc_injective_left Set.uIcc_injective_left
-/

end DistribLattice

section LinearOrder

variable [LinearOrder α] [LinearOrder β] {f : α → β} {s : Set α} {a a₁ a₂ b b₁ b₂ c d x : α}

/- warning: set.Icc_min_max -> Set.Icc_min_max is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (LinearOrder.min.{u1} α _inst_1 a b) (LinearOrder.max.{u1} α _inst_1 a b)) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) (Min.min.{u1} α (LinearOrder.toMin.{u1} α _inst_1) a b) (Max.max.{u1} α (LinearOrder.toMax.{u1} α _inst_1) a b)) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) a b)
Case conversion may be inaccurate. Consider using '#align set.Icc_min_max Set.Icc_min_maxₓ'. -/
theorem Icc_min_max : Icc (min a b) (max a b) = [a, b] :=
  rfl
#align set.Icc_min_max Set.Icc_min_max

#print Set.uIcc_of_not_le /-
theorem uIcc_of_not_le (h : ¬a ≤ b) : [a, b] = Icc b a :=
  uIcc_of_gt <| lt_of_not_ge h
#align set.uIcc_of_not_le Set.uIcc_of_not_le
-/

#print Set.uIcc_of_not_ge /-
theorem uIcc_of_not_ge (h : ¬b ≤ a) : [a, b] = Icc a b :=
  uIcc_of_lt <| lt_of_not_ge h
#align set.uIcc_of_not_ge Set.uIcc_of_not_ge
-/

/- warning: set.uIcc_eq_union -> Set.uIcc_eq_union is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a b) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) b a))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) a b) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) a b) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) b a))
Case conversion may be inaccurate. Consider using '#align set.uIcc_eq_union Set.uIcc_eq_unionₓ'. -/
theorem uIcc_eq_union : [a, b] = Icc a b ∪ Icc b a := by rw [Icc_union_Icc', max_comm] <;> rfl
#align set.uIcc_eq_union Set.uIcc_eq_union

#print Set.mem_uIcc /-
theorem mem_uIcc : a ∈ [b, c] ↔ b ≤ a ∧ a ≤ c ∨ c ≤ a ∧ a ≤ b := by simp [uIcc_eq_union]
#align set.mem_uIcc Set.mem_uIcc
-/

#print Set.not_mem_uIcc_of_lt /-
theorem not_mem_uIcc_of_lt (ha : c < a) (hb : c < b) : c ∉ [a, b] :=
  not_mem_Icc_of_lt <| lt_min_iff.mpr ⟨ha, hb⟩
#align set.not_mem_uIcc_of_lt Set.not_mem_uIcc_of_lt
-/

#print Set.not_mem_uIcc_of_gt /-
theorem not_mem_uIcc_of_gt (ha : a < c) (hb : b < c) : c ∉ [a, b] :=
  not_mem_Icc_of_gt <| max_lt_iff.mpr ⟨ha, hb⟩
#align set.not_mem_uIcc_of_gt Set.not_mem_uIcc_of_gt
-/

/- warning: set.uIcc_subset_uIcc_iff_le -> Set.uIcc_subset_uIcc_iff_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a₁ b₁) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a₂ b₂)) (And (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (LinearOrder.min.{u1} α _inst_1 a₂ b₂) (LinearOrder.min.{u1} α _inst_1 a₁ b₁)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (LinearOrder.max.{u1} α _inst_1 a₁ b₁) (LinearOrder.max.{u1} α _inst_1 a₂ b₂)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a₁ : α} {a₂ : α} {b₁ : α} {b₂ : α}, Iff (HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) a₁ b₁) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) a₂ b₂)) (And (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) (Min.min.{u1} α (LinearOrder.toMin.{u1} α _inst_1) a₂ b₂) (Min.min.{u1} α (LinearOrder.toMin.{u1} α _inst_1) a₁ b₁)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) (Max.max.{u1} α (LinearOrder.toMax.{u1} α _inst_1) a₁ b₁) (Max.max.{u1} α (LinearOrder.toMax.{u1} α _inst_1) a₂ b₂)))
Case conversion may be inaccurate. Consider using '#align set.uIcc_subset_uIcc_iff_le Set.uIcc_subset_uIcc_iff_leₓ'. -/
theorem uIcc_subset_uIcc_iff_le :
    [a₁, b₁] ⊆ [a₂, b₂] ↔ min a₂ b₂ ≤ min a₁ b₁ ∧ max a₁ b₁ ≤ max a₂ b₂ :=
  uIcc_subset_uIcc_iff_le'
#align set.uIcc_subset_uIcc_iff_le Set.uIcc_subset_uIcc_iff_le

/- warning: set.uIcc_subset_uIcc_union_uIcc -> Set.uIcc_subset_uIcc_union_uIcc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} {c : α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a c) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a b) (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) b c))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} {c : α}, HasSubset.Subset.{u1} (Set.{u1} α) (Set.instHasSubsetSet.{u1} α) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) a c) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) a b) (Set.uIcc.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)) b c))
Case conversion may be inaccurate. Consider using '#align set.uIcc_subset_uIcc_union_uIcc Set.uIcc_subset_uIcc_union_uIccₓ'. -/
/-- A sort of triangle inequality. -/
theorem uIcc_subset_uIcc_union_uIcc : [a, c] ⊆ [a, b] ∪ [b, c] := fun x => by
  simp only [mem_uIcc, mem_union] <;> cases le_total a c <;> cases le_total x b <;> tauto
#align set.uIcc_subset_uIcc_union_uIcc Set.uIcc_subset_uIcc_union_uIcc

/- warning: set.monotone_or_antitone_iff_uIcc -> Set.monotone_or_antitone_iff_uIcc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : LinearOrder.{u2} β] {f : α -> β}, Iff (Or (Monotone.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))) f) (Antitone.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))) f)) (forall (a : α) (b : α) (c : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a b)) -> (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (f c) (Set.uIcc.{u2} β (LinearOrder.toLattice.{u2} β _inst_2) (f a) (f b))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : LinearOrder.{u1} β] {f : α -> β}, Iff (Or (Monotone.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))) f) (Antitone.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))) f)) (forall (a : α) (b : α) (c : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) c (Set.uIcc.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)) a b)) -> (Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) (f c) (Set.uIcc.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2)) (f a) (f b))))
Case conversion may be inaccurate. Consider using '#align set.monotone_or_antitone_iff_uIcc Set.monotone_or_antitone_iff_uIccₓ'. -/
theorem monotone_or_antitone_iff_uIcc :
    Monotone f ∨ Antitone f ↔ ∀ a b c, c ∈ [a, b] → f c ∈ [f a, f b] :=
  by
  constructor
  · rintro (hf | hf) a b c <;> simp_rw [← Icc_min_max, ← hf.map_min, ← hf.map_max]
    exacts[fun hc => ⟨hf hc.1, hf hc.2⟩, fun hc => ⟨hf hc.2, hf hc.1⟩]
  contrapose!
  rw [not_monotone_not_antitone_iff_exists_le_le]
  rintro ⟨a, b, c, hab, hbc, ⟨hfab, hfcb⟩ | ⟨hfba, hfbc⟩⟩
  · exact ⟨a, c, b, Icc_subset_uIcc ⟨hab, hbc⟩, fun h => h.2.not_lt <| max_lt hfab hfcb⟩
  · exact ⟨a, c, b, Icc_subset_uIcc ⟨hab, hbc⟩, fun h => h.1.not_lt <| lt_min hfba hfbc⟩
#align set.monotone_or_antitone_iff_uIcc Set.monotone_or_antitone_iff_uIcc

/- warning: set.monotone_on_or_antitone_on_iff_uIcc -> Set.monotoneOn_or_antitoneOn_iff_uIcc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : LinearOrder.{u2} β] {f : α -> β} {s : Set.{u1} α}, Iff (Or (MonotoneOn.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))) f s) (AntitoneOn.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))) f s)) (forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (forall (b : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b s) -> (forall (c : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c s) -> (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) c (Set.uIcc.{u1} α (LinearOrder.toLattice.{u1} α _inst_1) a b)) -> (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (f c) (Set.uIcc.{u2} β (LinearOrder.toLattice.{u2} β _inst_2) (f a) (f b))))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : LinearOrder.{u1} β] {f : α -> β} {s : Set.{u2} α}, Iff (Or (MonotoneOn.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))) f s) (AntitoneOn.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))) f s)) (forall (a : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a s) -> (forall (b : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) b s) -> (forall (c : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) c s) -> (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) c (Set.uIcc.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)) a b)) -> (Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) (f c) (Set.uIcc.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2)) (f a) (f b))))))
Case conversion may be inaccurate. Consider using '#align set.monotone_on_or_antitone_on_iff_uIcc Set.monotoneOn_or_antitoneOn_iff_uIccₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (a b c «expr ∈ » s) -/
theorem monotoneOn_or_antitoneOn_iff_uIcc :
    MonotoneOn f s ∨ AntitoneOn f s ↔
      ∀ (a) (_ : a ∈ s) (b) (_ : b ∈ s) (c) (_ : c ∈ s), c ∈ [a, b] → f c ∈ [f a, f b] :=
  by
  simp [monotone_on_iff_monotone, antitone_on_iff_antitone, monotone_or_antitone_iff_uIcc, mem_uIcc]
#align set.monotone_on_or_antitone_on_iff_uIcc Set.monotoneOn_or_antitoneOn_iff_uIcc

#print Set.uIoc /-
/-- The open-closed interval with unordered bounds. -/
def uIoc : α → α → Set α := fun a b => Ioc (min a b) (max a b)
#align set.uIoc Set.uIoc
-/

-- mathport name: exprΙ
-- Below is a capital iota
scoped[Interval] notation "Ι" => Set.uIoc

#print Set.uIoc_of_le /-
@[simp]
theorem uIoc_of_le (h : a ≤ b) : Ι a b = Ioc a b := by simp [uIoc, h]
#align set.uIoc_of_le Set.uIoc_of_le
-/

#print Set.uIoc_of_lt /-
@[simp]
theorem uIoc_of_lt (h : b < a) : Ι a b = Ioc b a := by simp [uIoc, h.le]
#align set.uIoc_of_lt Set.uIoc_of_lt
-/

/- warning: set.uIoc_eq_union -> Set.uIoc_eq_union is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (Set.uIoc.{u1} α _inst_1 a b) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (Set.Ioc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b) (Set.Ioc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) b a))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α}, Eq.{succ u1} (Set.{u1} α) (Set.uIoc.{u1} α _inst_1 a b) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (Set.Ioc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) a b) (Set.Ioc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) b a))
Case conversion may be inaccurate. Consider using '#align set.uIoc_eq_union Set.uIoc_eq_unionₓ'. -/
theorem uIoc_eq_union : Ι a b = Ioc a b ∪ Ioc b a := by cases le_total a b <;> simp [uIoc, *]
#align set.uIoc_eq_union Set.uIoc_eq_union

#print Set.mem_uIoc /-
theorem mem_uIoc : a ∈ Ι b c ↔ b < a ∧ a ≤ c ∨ c < a ∧ a ≤ b := by
  simp only [uIoc_eq_union, mem_union, mem_Ioc]
#align set.mem_uIoc Set.mem_uIoc
-/

#print Set.not_mem_uIoc /-
theorem not_mem_uIoc : a ∉ Ι b c ↔ a ≤ b ∧ a ≤ c ∨ c < a ∧ b < a :=
  by
  simp only [uIoc_eq_union, mem_union, mem_Ioc, not_lt, ← not_le]
  tauto
#align set.not_mem_uIoc Set.not_mem_uIoc
-/

#print Set.left_mem_uIoc /-
@[simp]
theorem left_mem_uIoc : a ∈ Ι a b ↔ b < a := by simp [mem_uIoc]
#align set.left_mem_uIoc Set.left_mem_uIoc
-/

#print Set.right_mem_uIoc /-
@[simp]
theorem right_mem_uIoc : b ∈ Ι a b ↔ a < b := by simp [mem_uIoc]
#align set.right_mem_uIoc Set.right_mem_uIoc
-/

#print Set.forall_uIoc_iff /-
theorem forall_uIoc_iff {P : α → Prop} :
    (∀ x ∈ Ι a b, P x) ↔ (∀ x ∈ Ioc a b, P x) ∧ ∀ x ∈ Ioc b a, P x := by
  simp only [uIoc_eq_union, mem_union, or_imp, forall_and]
#align set.forall_uIoc_iff Set.forall_uIoc_iff
-/

#print Set.uIoc_subset_uIoc_of_uIcc_subset_uIcc /-
theorem uIoc_subset_uIoc_of_uIcc_subset_uIcc (h : [a, b] ⊆ [c, d]) : Ι a b ⊆ Ι c d :=
  Ioc_subset_Ioc (uIcc_subset_uIcc_iff_le.1 h).1 (uIcc_subset_uIcc_iff_le.1 h).2
#align set.uIoc_subset_uIoc_of_uIcc_subset_uIcc Set.uIoc_subset_uIoc_of_uIcc_subset_uIcc
-/

theorem uIoc_swap (a b : α) : Ι a b = Ι b a := by simp only [uIoc, min_comm a b, max_comm a b]
#align set.uIoc_swap Set.uIoc_swap

#print Set.Ioc_subset_uIoc /-
theorem Ioc_subset_uIoc : Ioc a b ⊆ Ι a b :=
  Ioc_subset_Ioc (min_le_left _ _) (le_max_right _ _)
#align set.Ioc_subset_uIoc Set.Ioc_subset_uIoc
-/

#print Set.Ioc_subset_uIoc' /-
theorem Ioc_subset_uIoc' : Ioc a b ⊆ Ι b a :=
  Ioc_subset_Ioc (min_le_right _ _) (le_max_left _ _)
#align set.Ioc_subset_uIoc' Set.Ioc_subset_uIoc'
-/

#print Set.eq_of_mem_uIoc_of_mem_uIoc /-
theorem eq_of_mem_uIoc_of_mem_uIoc : a ∈ Ι b c → b ∈ Ι a c → a = b := by
  simp_rw [mem_uIoc] <;> rintro (⟨_, _⟩ | ⟨_, _⟩) (⟨_, _⟩ | ⟨_, _⟩) <;> apply le_antisymm <;>
    first |assumption|exact le_of_lt ‹_›|exact le_trans ‹_› (le_of_lt ‹_›)
#align set.eq_of_mem_uIoc_of_mem_uIoc Set.eq_of_mem_uIoc_of_mem_uIoc
-/

#print Set.eq_of_mem_uIoc_of_mem_uIoc' /-
theorem eq_of_mem_uIoc_of_mem_uIoc' : b ∈ Ι a c → c ∈ Ι a b → b = c := by
  simpa only [uIoc_swap a] using eq_of_mem_uIoc_of_mem_uIoc
#align set.eq_of_mem_uIoc_of_mem_uIoc' Set.eq_of_mem_uIoc_of_mem_uIoc'
-/

#print Set.eq_of_not_mem_uIoc_of_not_mem_uIoc /-
theorem eq_of_not_mem_uIoc_of_not_mem_uIoc (ha : a ≤ c) (hb : b ≤ c) :
    a ∉ Ι b c → b ∉ Ι a c → a = b := by
  simp_rw [not_mem_uIoc] <;> rintro (⟨_, _⟩ | ⟨_, _⟩) (⟨_, _⟩ | ⟨_, _⟩) <;> apply le_antisymm <;>
    first |assumption|exact le_of_lt ‹_›|cases not_le_of_lt ‹_› ‹_›
#align set.eq_of_not_mem_uIoc_of_not_mem_uIoc Set.eq_of_not_mem_uIoc_of_not_mem_uIoc
-/

#print Set.uIoc_injective_right /-
theorem uIoc_injective_right (a : α) : Injective fun b => Ι b a :=
  by
  rintro b c h
  rw [ext_iff] at h
  obtain ha | ha := le_or_lt b a
  · have hb := (h b).Not
    simp only [ha, left_mem_uIoc, not_lt, true_iff_iff, not_mem_uIoc, ← not_le, and_true_iff,
      not_true, false_and_iff, not_false_iff, true_iff_iff, or_false_iff] at hb
    refine' hb.eq_of_not_lt fun hc => _
    simpa [ha, and_iff_right hc, ← @not_le _ _ _ a, -not_le] using h c
  · refine'
      eq_of_mem_uIoc_of_mem_uIoc ((h _).1 <| left_mem_uIoc.2 ha)
        ((h _).2 <| left_mem_uIoc.2 <| ha.trans_le _)
    simpa [ha, ha.not_le, mem_uIoc] using h b
#align set.uIoc_injective_right Set.uIoc_injective_right
-/

#print Set.uIoc_injective_left /-
theorem uIoc_injective_left (a : α) : Injective (Ι a) := by
  simpa only [uIoc_swap] using uIoc_injective_right a
#align set.uIoc_injective_left Set.uIoc_injective_left
-/

end LinearOrder

end Set


/-
Copyright (c) 2022 Violeta Hernández Palacios. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Violeta Hernández Palacios
-/

import set_theory.zfc.basic

/-!
# Von Neumann ordinals

This file works towards the development of von Neumann ordinals, i.e. transitive sets, well-ordered
under `∈`. We currently only have an initial development of transitive sets.

Further development can be found on the branch `von_neumann_v2`.

## Definitions

- `Set.is_transitive` means that every element of a set is a subset.

## Todo

- Define von Neumann ordinals.
- Define the basic arithmetic operations on ordinals from a purely set-theoretic perspective.
- Prove the equivalences between these definitions and those provided in
  `set_theory/ordinal/arithmetic.lean`.
-/

universe u

variables {x y z : Set.{u}}

namespace Set

/-- A transitive set is one where every element is a subset. -/
def is_transitive (x : Set) : Prop := ∀ y ∈ x, y ⊆ x

@[simp] theorem empty_is_transitive : is_transitive ∅ := λ y hy, (not_mem_empty y hy).elim

theorem is_transitive.subset_of_mem (h : x.is_transitive) : y ∈ x → y ⊆ x := h y

theorem is_transitive_iff_mem_trans : z.is_transitive ↔ ∀ {x y : Set}, x ∈ y → y ∈ z → x ∈ z :=
⟨λ h x y hx hy, h.subset_of_mem hy hx, λ H x hx y hy, H hy hx⟩

alias is_transitive_iff_mem_trans ↔ is_transitive.mem_trans _

protected theorem is_transitive.inter (hx : x.is_transitive) (hy : y.is_transitive) :
  (x ∩ y).is_transitive :=
λ z hz w hw, by { rw mem_inter at hz ⊢, exact ⟨hx.mem_trans hw hz.1, hy.mem_trans hw hz.2⟩ }

protected theorem is_transitive.sUnion (h : x.is_transitive) : (⋃₀ x).is_transitive :=
λ y hy z hz, begin
  rcases mem_sUnion.1 hy with ⟨w, hw, hw'⟩,
  exact mem_sUnion_of_mem hz (h.mem_trans hw' hw)
end

theorem is_transitive.sUnion' (H : ∀ y ∈ x, is_transitive y) : (⋃₀ x).is_transitive :=
λ y hy z hz, begin
  rcases mem_sUnion.1 hy with ⟨w, hw, hw'⟩,
  exact mem_sUnion_of_mem ((H w hw).mem_trans hz hw') hw
end

protected theorem is_transitive.union (hx : x.is_transitive) (hy : y.is_transitive) :
  (x ∪ y).is_transitive :=
begin
  rw ←sUnion_pair,
  apply is_transitive.sUnion' (λ z, _),
  rw mem_pair,
  rintro (rfl | rfl),
  assumption'
end

protected theorem is_transitive.powerset (h : x.is_transitive) : (powerset x).is_transitive :=
λ y hy z hz, by { rw mem_powerset at ⊢ hy, exact h.subset_of_mem (hy hz) }

theorem is_transitive_iff_sUnion_subset : x.is_transitive ↔ ⋃₀ x ⊆ x :=
⟨λ h y hy, by { rcases mem_sUnion.1 hy with ⟨z, hz, hz'⟩, exact h.mem_trans hz' hz },
  λ H y hy z hz, H $ mem_sUnion_of_mem hz hy⟩

alias is_transitive_iff_sUnion_subset ↔ is_transitive.sUnion_subset _

theorem is_transitive_iff_subset_powerset : x.is_transitive ↔ x ⊆ powerset x :=
⟨λ h y hy, mem_powerset.2 $ h.subset_of_mem hy, λ H y hy z hz, mem_powerset.1 (H hy) hz⟩

alias is_transitive_iff_subset_powerset ↔ is_transitive.subset_powerset _

end Set