/-
Copyright (c) 2022 Bolton Bailey. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bolton Bailey, Patrick Stevens, Thomas Browning

! This file was ported from Lean 3 source module data.nat.choose.factorization
! leanprover-community/mathlib commit dc9db541168768af03fe228703e758e649afdbfc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Nat.Choose.Central
import Mathlib.Data.Nat.Factorization.Basic
import Mathlib.Data.Nat.Multiplicity

/-!
# Factorization of Binomial Coefficients

This file contains a few results on the multiplicity of prime factors within certain size
bounds in binomial coefficients. These include:

* `Nat.factorization_choose_le_log`: a logarithmic upper bound on the multiplicity of a prime in
  a binomial coefficient.
* `Nat.factorization_choose_le_one`: Primes above `sqrt n` appear at most once
  in the factorization of `n` choose `k`.
* `Nat.factorization_centralBinom_of_two_mul_self_lt_three_mul`: Primes from `2 * n / 3` to `n`
do not appear in the factorization of the `n`th central binomial coefficient.
* `Nat.factorization_choose_eq_zero_of_lt`: Primes greater than `n` do not
  appear in the factorization of `n` choose `k`.

These results appear in the [Erdős proof of Bertrand's postulate](aigner1999proofs).
-/

open BigOperators

namespace Nat

variable {p n k : ℕ}

/-- A logarithmic upper bound on the multiplicity of a prime in a binomial coefficient. -/
theorem factorization_choose_le_log : (choose n k).factorization p ≤ log p n := by
  by_cases h : (choose n k).factorization p = 0
  · simp [h]
  have hp : p.Prime := Not.imp_symm (choose n k).factorization_eq_zero_of_non_prime h
  have hkn : k ≤ n := by
    refine' le_of_not_lt fun hnk => h _
    simp [choose_eq_zero_of_lt hnk]
  rw [factorization_def _ hp, @padicValNat_def _ ⟨hp⟩ _ (choose_pos hkn)]
  simp only [hp.multiplicity_choose hkn (lt_add_one _), PartENat.get_natCast]
  refine (Finset.card_filter_le _ _).trans (le_of_eq (Nat.card_Ico _ _))
#align nat.factorization_choose_le_log Nat.factorization_choose_le_log

/-- A `pow` form of `Nat.factorization_choose_le` -/
theorem pow_factorization_choose_le (hn : 0 < n) : p ^ (choose n k).factorization p ≤ n :=
  pow_le_of_le_log hn.ne' factorization_choose_le_log
#align nat.pow_factorization_choose_le Nat.pow_factorization_choose_le

/-- Primes greater than about `sqrt n` appear only to multiplicity 0 or 1
in the binomial coefficient. -/
theorem factorization_choose_le_one (p_large : n < p ^ 2) : (choose n k).factorization p ≤ 1 := by
  apply factorization_choose_le_log.trans
  rcases eq_or_ne n 0 with (rfl | hn0); · simp
  exact lt_succ_iff.1 (log_lt_of_lt_pow hn0 p_large)
#align nat.factorization_choose_le_one Nat.factorization_choose_le_one

theorem factorization_choose_of_lt_three_mul (hp' : p ≠ 2) (hk : p ≤ k) (hk' : p ≤ n - k)
    (hn : n < 3 * p) : (choose n k).factorization p = 0 := by
  cases' em' p.Prime with hp hp
  · exact factorization_eq_zero_of_non_prime (choose n k) hp
  cases' lt_or_le n k with hnk hkn
  · simp [choose_eq_zero_of_lt hnk]
  rw [factorization_def _ hp, @padicValNat_def _ ⟨hp⟩ _ (choose_pos hkn)]
  simp only [hp.multiplicity_choose hkn (lt_add_one _), PartENat.get_natCast, Finset.card_eq_zero,
    Finset.filter_eq_empty_iff, not_le]
  intro i hi
  rcases eq_or_lt_of_le (Finset.mem_Ico.mp hi).1 with (rfl | hi)
  · rw [pow_one, ← add_lt_add_iff_left (2 * p), ← succ_mul, two_mul, add_add_add_comm]
    exact
      lt_of_le_of_lt
        (add_le_add
          (add_le_add_right (le_mul_of_one_le_right' ((one_le_div_iff hp.pos).mpr hk)) (k % p))
          (add_le_add_right (le_mul_of_one_le_right' ((one_le_div_iff hp.pos).mpr hk'))
            ((n - k) % p)))
        (by rwa [div_add_mod, div_add_mod, add_tsub_cancel_of_le hkn])
  · replace hn : n < p ^ i
    have : 3 ≤ p := lt_of_le_of_ne hp.two_le hp'.symm
    · calc
        n < 3 * p := hn
        _ ≤ p * p := mul_le_mul_right' this p
        _ = p ^ 2 := (sq p).symm
        _ ≤ p ^ i := pow_le_pow hp.one_lt.le hi
    rwa [mod_eq_of_lt (lt_of_le_of_lt hkn hn), mod_eq_of_lt (lt_of_le_of_lt tsub_le_self hn),
      add_tsub_cancel_of_le hkn]
#align nat.factorization_choose_of_lt_three_mul Nat.factorization_choose_of_lt_three_mul

/-- Primes greater than about `2 * n / 3` and less than `n` do not appear in the factorization of
`centralBinom n`. -/
theorem factorization_centralBinom_of_two_mul_self_lt_three_mul (n_big : 2 < n) (p_le_n : p ≤ n)
    (big : 2 * n < 3 * p) : (centralBinom n).factorization p = 0 := by
  refine' factorization_choose_of_lt_three_mul _ p_le_n (p_le_n.trans _) big
  · rintro rfl
    linarith
  · rw [two_mul, add_tsub_cancel_left]
#align nat.factorization_central_binom_of_two_mul_self_lt_three_mul Nat.factorization_centralBinom_of_two_mul_self_lt_three_mul

theorem factorization_factorial_eq_zero_of_lt (h : n < p) : (factorial n).factorization p = 0 := by
  induction' n with n hn; · simp
  rw [factorial_succ, factorization_mul n.succ_ne_zero n.factorial_ne_zero, Finsupp.coe_add,
    Pi.add_apply, hn (lt_of_succ_lt h), add_zero, factorization_eq_zero_of_lt h]
#align nat.factorization_factorial_eq_zero_of_lt Nat.factorization_factorial_eq_zero_of_lt

theorem factorization_choose_eq_zero_of_lt (h : n < p) : (choose n k).factorization p = 0 := by
  by_cases hnk : n < k; · simp [choose_eq_zero_of_lt hnk]
  rw [choose_eq_factorial_div_factorial (le_of_not_lt hnk),
    factorization_div (factorial_mul_factorial_dvd_factorial (le_of_not_lt hnk)), Finsupp.coe_tsub,
    Pi.sub_apply, factorization_factorial_eq_zero_of_lt h, zero_tsub]
#align nat.factorization_choose_eq_zero_of_lt Nat.factorization_choose_eq_zero_of_lt

/-- If a prime `p` has positive multiplicity in the `n`th central binomial coefficient,
`p` is no more than `2 * n` -/
theorem factorization_centralBinom_eq_zero_of_two_mul_lt (h : 2 * n < p) :
    (centralBinom n).factorization p = 0 :=
  factorization_choose_eq_zero_of_lt h
#align nat.factorization_central_binom_eq_zero_of_two_mul_lt Nat.factorization_centralBinom_eq_zero_of_two_mul_lt

/-- Contrapositive form of `Nat.factorization_centralBinom_eq_zero_of_two_mul_lt` -/
theorem le_two_mul_of_factorization_centralBinom_pos
    (h_pos : 0 < (centralBinom n).factorization p) : p ≤ 2 * n :=
  le_of_not_lt (pos_iff_ne_zero.mp h_pos ∘ factorization_centralBinom_eq_zero_of_two_mul_lt)
#align nat.le_two_mul_of_factorization_central_binom_pos Nat.le_two_mul_of_factorization_centralBinom_pos

/-- A binomial coefficient is the product of its prime factors, which are at most `n`. -/
theorem prod_pow_factorization_choose (n k : ℕ) (hkn : k ≤ n) :
    (∏ p in Finset.range (n + 1), p ^ (Nat.choose n k).factorization p) = choose n k := by
  conv => -- Porting note: was `nth_rw_rhs`
    rhs
    rw [← factorization_prod_pow_eq_self (choose_pos hkn).ne']
  rw [eq_comm]
  apply Finset.prod_subset
  · intro p hp
    rw [Finset.mem_range]
    contrapose! hp
    rw [Finsupp.mem_support_iff, Classical.not_not, factorization_choose_eq_zero_of_lt hp]
  · intro p _ h2
    simp [Classical.not_not.1 (mt Finsupp.mem_support_iff.2 h2)]
#align nat.prod_pow_factorization_choose Nat.prod_pow_factorization_choose

/-- The `n`th central binomial coefficient is the product of its prime factors, which are
at most `2n`. -/
theorem prod_pow_factorization_centralBinom (n : ℕ) :
    (∏ p in Finset.range (2 * n + 1), p ^ (centralBinom n).factorization p) = centralBinom n := by
  apply prod_pow_factorization_choose
  linarith
#align nat.prod_pow_factorization_central_binom Nat.prod_pow_factorization_centralBinom

end Nat


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

import ring_theory.principal_ideal_domain
import algebra.gcd_monoid.integrally_closed

/-!

# Bézout rings

A Bézout ring (Bezout ring) is a ring whose finitely generated ideals are principal.
Notible examples include principal ideal rings, valuation rings, and the ring of algebraic integers.

## Main results
- `is_bezout.iff_span_pair_is_principal`: It suffices to verify every `span {x, y}` is principal.
- `is_bezout.to_gcd_monoid`: Every Bézout domain is a GCD domain. This is not an instance.
- `is_bezout.tfae`: For a Bézout domain, noetherian ↔ PID ↔ UFD ↔ ACCP

-/

universes u v

variables (R : Type u) [comm_ring R]

/-- A Bézout ring is a ring whose finitely generated ideals are principal. -/
class is_bezout : Prop :=
(is_principal_of_fg : ∀ I : ideal R, I.fg → I.is_principal)

namespace is_bezout

variables {R}

instance span_pair_is_principal [is_bezout R] (x y : R) :
  (ideal.span {x, y} : ideal R).is_principal :=
by { classical, exact is_principal_of_fg (ideal.span {x, y}) ⟨{x, y}, by simp⟩ }

lemma iff_span_pair_is_principal :
  is_bezout R ↔ (∀ x y : R, (ideal.span {x, y} : ideal R).is_principal) :=
begin
  classical,
  split,
  { introsI H x y, apply_instance },
  { intro H,
    constructor,
    apply submodule.fg_induction,
    { exact λ _, ⟨⟨_, rfl⟩⟩ },
    { rintro _ _ ⟨⟨x, rfl⟩⟩ ⟨⟨y, rfl⟩⟩, rw ← submodule.span_insert, exact H _ _ } },
end

section gcd

variable [is_bezout R]

/-- The gcd of two elements in a bezout domain. -/
noncomputable
def gcd (x y : R) : R :=
submodule.is_principal.generator (ideal.span {x, y})

lemma span_gcd (x y : R) : (ideal.span {gcd x y} : ideal R) = ideal.span {x, y} :=
ideal.span_singleton_generator _

lemma gcd_dvd_left (x y : R) : gcd x y ∣ x :=
(submodule.is_principal.mem_iff_generator_dvd _).mp (ideal.subset_span (by simp))

lemma gcd_dvd_right (x y : R) : gcd x y ∣ y :=
(submodule.is_principal.mem_iff_generator_dvd _).mp (ideal.subset_span (by simp))

lemma dvd_gcd {x y z : R} (hx : z ∣ x) (hy : z ∣ y) : z ∣ gcd x y :=
begin
  rw [← ideal.span_singleton_le_span_singleton] at hx hy ⊢,
  rw [span_gcd, ideal.span_insert, sup_le_iff],
  exact ⟨hx, hy⟩
end

lemma gcd_eq_sum (x y : R) : ∃ a b : R, a * x + b * y = gcd x y :=
ideal.mem_span_pair.mp (by { rw ← span_gcd, apply ideal.subset_span, simp })

variable (R)

/-- Any bezout domain is a GCD domain. This is not an instance since `gcd_monoid` contains data,
and this might not be how we would like to construct it. -/
noncomputable
def to_gcd_domain [is_domain R] [decidable_eq R] :
  gcd_monoid R :=
gcd_monoid_of_gcd gcd gcd_dvd_left gcd_dvd_right
  (λ _ _ _, dvd_gcd)

end gcd

local attribute [instance] to_gcd_domain

-- Note that the proof, despite being `infer_instance`, depends on the `local attribute [instance]`
-- lemma above, and is thus necessary to be restated.
@[priority 100]
instance [is_domain R] [is_bezout R] : is_integrally_closed R :=
by classical; exact gcd_monoid.to_is_integrally_closed

lemma _root_.function.surjective.is_bezout {S : Type v} [comm_ring S] (f : R →+* S)
  (hf : function.surjective f) [is_bezout R] : is_bezout S :=
begin
  rw iff_span_pair_is_principal,
  intros x y,
  obtain ⟨⟨x, rfl⟩, ⟨y, rfl⟩⟩ := ⟨hf x, hf y⟩,
  use f (gcd x y),
  transitivity ideal.map f (ideal.span {gcd x y}),
  { rw [span_gcd, ideal.map_span, set.image_insert_eq, set.image_singleton] },
  { rw [ideal.map_span, set.image_singleton], refl }
end

@[priority 100]
instance of_is_principal_ideal_ring [is_principal_ideal_ring R] : is_bezout R :=
⟨λ I _, is_principal_ideal_ring.principal I⟩

lemma tfae [is_bezout R] [is_domain R] :
  tfae [is_noetherian_ring R,
    is_principal_ideal_ring R,
    unique_factorization_monoid R,
    wf_dvd_monoid R] :=
begin
  classical,
  tfae_have : 1 → 2,
  { introI H, exact ⟨λ I, is_principal_of_fg _ (is_noetherian.noetherian _)⟩ },
  tfae_have : 2 → 3,
  { introI _, apply_instance },
  tfae_have : 3 → 4,
  { introI _, apply_instance },
  tfae_have : 4 → 1,
  { rintro ⟨h⟩,
    rw [is_noetherian_ring_iff, is_noetherian_iff_fg_well_founded],
    apply rel_embedding.well_founded _ h,
    have : ∀ I : { J : ideal R // J.fg }, ∃ x : R, (I : ideal R) = ideal.span {x} :=
      λ ⟨I, hI⟩, (is_bezout.is_principal_of_fg I hI).1,
    choose f hf,
    exact
    { to_fun := f,
      inj' := λ x y e, by { ext1, rw [hf, hf, e] },
      map_rel_iff' := λ x y,
      by { dsimp, rw [← ideal.span_singleton_lt_span_singleton, ← hf, ← hf], refl } } },
  tfae_finish
end

end is_bezout


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.bases
import Mathlib.data.finset.order
import Mathlib.PostPort

universes u v u_1 l u_2

namespace Mathlib

/-!
# Properties of subsets of topological spaces

In this file we define various properties of subsets of a topological space, and some classes on
topological spaces.

## Main definitions

We define the following properties for sets in a topological space:

* `is_compact`: each open cover has a finite subcover. This is defined in mathlib using filters.
  The main property of a compact set is `is_compact.elim_finite_subcover`.
* `is_clopen`: a set that is both open and closed.
* `is_irreducible`: a nonempty set that has contains no non-trivial pair of disjoint opens.
  See also the section below in the module doc.
* `is_connected`: a nonempty set that has no non-trivial open partition.
  See also the section below in the module doc.
  `connected_component` is the connected component of an element in the space.
* `is_totally_disconnected`: all of its connected components are singletons.
* `is_totally_separated`: any two points can be separated by two disjoint opens that cover the set.

For each of these definitions (except for `is_clopen`), we also have a class stating that the whole
space satisfies that property:
`compact_space`, `irreducible_space`, `connected_space`, `totally_disconnected_space`,
`totally_separated_space`.

Furthermore, we have two more classes:
* `locally_compact_space`: for every point `x`, every open neighborhood of `x` contains a compact
  neighborhood of `x`. The definition is formulated in terms of the neighborhood filter.
* `sigma_compact_space`: a space that is the union of a countably many compact subspaces.

## On the definition of irreducible and connected sets/spaces

In informal mathematics, irreducible and connected spaces are assumed to be nonempty.
We formalise the predicate without that assumption
as `is_preirreducible` and `is_preconnected` respectively.
In other words, the only difference is whether the empty space
counts as irreducible and/or connected.
There are good reasons to consider the empty space to be “too simple to be simple”
See also https://ncatlab.org/nlab/show/too+simple+to+be+simple,
and in particular
https://ncatlab.org/nlab/show/too+simple+to+be+simple#relationship_to_biased_definitions.
-/

/- compact sets -/

/-- A set `s` is compact if for every filter `f` that contains `s`,
    every set of `f` also meets every neighborhood of some `a ∈ s`. -/
def is_compact {α : Type u} [topological_space α] (s : set α) :=
  ∀ {f : filter α} [_inst_2 : filter.ne_bot f],
    f ≤ filter.principal s → ∃ (a : α), ∃ (H : a ∈ s), cluster_pt a f

/-- The complement to a compact set belongs to a filter `f` if it belongs to each filter
`𝓝 a ⊓ f`, `a ∈ s`. -/
theorem is_compact.compl_mem_sets {α : Type u} [topological_space α] {s : set α} (hs : is_compact s)
    {f : filter α} (hf : ∀ (a : α), a ∈ s → sᶜ ∈ nhds a ⊓ f) : sᶜ ∈ f :=
  sorry

/-- The complement to a compact set belongs to a filter `f` if each `a ∈ s` has a neighborhood `t`
within `s` such that `tᶜ` belongs to `f`. -/
theorem is_compact.compl_mem_sets_of_nhds_within {α : Type u} [topological_space α] {s : set α}
    (hs : is_compact s) {f : filter α}
    (hf : ∀ (a : α) (H : a ∈ s), ∃ (t : set α), ∃ (H : t ∈ nhds_within a s), tᶜ ∈ f) : sᶜ ∈ f :=
  sorry

/-- If `p : set α → Prop` is stable under restriction and union, and each point `x`
  of a compact set `s` has a neighborhood `t` within `s` such that `p t`, then `p s` holds. -/
theorem is_compact.induction_on {α : Type u} [topological_space α] {s : set α} (hs : is_compact s)
    {p : set α → Prop} (he : p ∅) (hmono : ∀ {s t : set α}, s ⊆ t → p t → p s)
    (hunion : ∀ {s t : set α}, p s → p t → p (s ∪ t))
    (hnhds : ∀ (x : α) (H : x ∈ s), ∃ (t : set α), ∃ (H : t ∈ nhds_within x s), p t) : p s :=
  sorry

/-- The intersection of a compact set and a closed set is a compact set. -/
theorem is_compact.inter_right {α : Type u} [topological_space α] {s : set α} {t : set α}
    (hs : is_compact s) (ht : is_closed t) : is_compact (s ∩ t) :=
  sorry

/-- The intersection of a closed set and a compact set is a compact set. -/
theorem is_compact.inter_left {α : Type u} [topological_space α] {s : set α} {t : set α}
    (ht : is_compact t) (hs : is_closed s) : is_compact (s ∩ t) :=
  set.inter_comm t s ▸ is_compact.inter_right ht hs

/-- The set difference of a compact set and an open set is a compact set. -/
theorem compact_diff {α : Type u} [topological_space α] {s : set α} {t : set α} (hs : is_compact s)
    (ht : is_open t) : is_compact (s \ t) :=
  is_compact.inter_right hs (iff.mpr is_closed_compl_iff ht)

/-- A closed subset of a compact set is a compact set. -/
theorem compact_of_is_closed_subset {α : Type u} [topological_space α] {s : set α} {t : set α}
    (hs : is_compact s) (ht : is_closed t) (h : t ⊆ s) : is_compact t :=
  set.inter_eq_self_of_subset_right h ▸ is_compact.inter_right hs ht

theorem is_compact.adherence_nhdset {α : Type u} [topological_space α] {s : set α} {t : set α}
    {f : filter α} (hs : is_compact s) (hf₂ : f ≤ filter.principal s) (ht₁ : is_open t)
    (ht₂ : ∀ (a : α), a ∈ s → cluster_pt a f → a ∈ t) : t ∈ f :=
  sorry

theorem compact_iff_ultrafilter_le_nhds {α : Type u} [topological_space α] {s : set α} :
    is_compact s ↔
        ∀ (f : ultrafilter α), ↑f ≤ filter.principal s → ∃ (a : α), ∃ (H : a ∈ s), ↑f ≤ nhds a :=
  sorry

theorem is_compact.ultrafilter_le_nhds {α : Type u} [topological_space α] {s : set α} :
    is_compact s →
        ∀ (f : ultrafilter α), ↑f ≤ filter.principal s → ∃ (a : α), ∃ (H : a ∈ s), ↑f ≤ nhds a :=
  iff.mp compact_iff_ultrafilter_le_nhds

/-- For every open cover of a compact set, there exists a finite subcover. -/
theorem is_compact.elim_finite_subcover {α : Type u} [topological_space α] {s : set α} {ι : Type v}
    (hs : is_compact s) (U : ι → set α) (hUo : ∀ (i : ι), is_open (U i))
    (hsU : s ⊆ set.Union fun (i : ι) => U i) :
    ∃ (t : finset ι), s ⊆ set.Union fun (i : ι) => set.Union fun (H : i ∈ t) => U i :=
  sorry

/-- For every family of closed sets whose intersection avoids a compact set,
there exists a finite subfamily whose intersection avoids this compact set. -/
theorem is_compact.elim_finite_subfamily_closed {α : Type u} [topological_space α] {s : set α}
    {ι : Type v} (hs : is_compact s) (Z : ι → set α) (hZc : ∀ (i : ι), is_closed (Z i))
    (hsZ : (s ∩ set.Inter fun (i : ι) => Z i) = ∅) :
    ∃ (t : finset ι), (s ∩ set.Inter fun (i : ι) => set.Inter fun (H : i ∈ t) => Z i) = ∅ :=
  sorry

/-- To show that a compact set intersects the intersection of a family of closed sets,
  it is sufficient to show that it intersects every finite subfamily. -/
theorem is_compact.inter_Inter_nonempty {α : Type u} [topological_space α] {s : set α} {ι : Type v}
    (hs : is_compact s) (Z : ι → set α) (hZc : ∀ (i : ι), is_closed (Z i))
    (hsZ :
      ∀ (t : finset ι),
        set.nonempty (s ∩ set.Inter fun (i : ι) => set.Inter fun (H : i ∈ t) => Z i)) :
    set.nonempty (s ∩ set.Inter fun (i : ι) => Z i) :=
  sorry

/-- Cantor's intersection theorem:
the intersection of a directed family of nonempty compact closed sets is nonempty. -/
theorem is_compact.nonempty_Inter_of_directed_nonempty_compact_closed {α : Type u}
    [topological_space α] {ι : Type v} [hι : Nonempty ι] (Z : ι → set α) (hZd : directed superset Z)
    (hZn : ∀ (i : ι), set.nonempty (Z i)) (hZc : ∀ (i : ι), is_compact (Z i))
    (hZcl : ∀ (i : ι), is_closed (Z i)) : set.nonempty (set.Inter fun (i : ι) => Z i) :=
  sorry

/-- Cantor's intersection theorem for sequences indexed by `ℕ`:
the intersection of a decreasing sequence of nonempty compact closed sets is nonempty. -/
theorem is_compact.nonempty_Inter_of_sequence_nonempty_compact_closed {α : Type u}
    [topological_space α] (Z : ℕ → set α) (hZd : ∀ (i : ℕ), Z (i + 1) ⊆ Z i)
    (hZn : ∀ (i : ℕ), set.nonempty (Z i)) (hZ0 : is_compact (Z 0))
    (hZcl : ∀ (i : ℕ), is_closed (Z i)) : set.nonempty (set.Inter fun (i : ℕ) => Z i) :=
  sorry

/-- For every open cover of a compact set, there exists a finite subcover. -/
theorem is_compact.elim_finite_subcover_image {α : Type u} {β : Type v} [topological_space α]
    {s : set α} {b : set β} {c : β → set α} (hs : is_compact s)
    (hc₁ : ∀ (i : β), i ∈ b → is_open (c i))
    (hc₂ : s ⊆ set.Union fun (i : β) => set.Union fun (H : i ∈ b) => c i) :
    ∃ (b' : set β),
        ∃ (H : b' ⊆ b),
          set.finite b' ∧ s ⊆ set.Union fun (i : β) => set.Union fun (H : i ∈ b') => c i :=
  sorry

/-- A set `s` is compact if for every family of closed sets whose intersection avoids `s`,
there exists a finite subfamily whose intersection avoids `s`. -/
theorem compact_of_finite_subfamily_closed {α : Type u} [topological_space α] {s : set α}
    (h :
      ∀ {ι : Type u} (Z : ι → set α),
        (∀ (i : ι), is_closed (Z i)) →
          (s ∩ set.Inter fun (i : ι) => Z i) = ∅ →
            ∃ (t : finset ι), (s ∩ set.Inter fun (i : ι) => set.Inter fun (H : i ∈ t) => Z i) = ∅) :
    is_compact s :=
  sorry

/-- A set `s` is compact if for every open cover of `s`, there exists a finite subcover. -/
theorem compact_of_finite_subcover {α : Type u} [topological_space α] {s : set α}
    (h :
      ∀ {ι : Type u} (U : ι → set α),
        (∀ (i : ι), is_open (U i)) →
          (s ⊆ set.Union fun (i : ι) => U i) →
            ∃ (t : finset ι), s ⊆ set.Union fun (i : ι) => set.Union fun (H : i ∈ t) => U i) :
    is_compact s :=
  sorry

/-- A set `s` is compact if and only if
for every open cover of `s`, there exists a finite subcover. -/
theorem compact_iff_finite_subcover {α : Type u} [topological_space α] {s : set α} :
    is_compact s ↔
        ∀ {ι : Type u} (U : ι → set α),
          (∀ (i : ι), is_open (U i)) →
            (s ⊆ set.Union fun (i : ι) => U i) →
              ∃ (t : finset ι), s ⊆ set.Union fun (i : ι) => set.Union fun (H : i ∈ t) => U i :=
  { mp := fun (hs : is_compact s) (ι : Type u) => is_compact.elim_finite_subcover hs,
    mpr := compact_of_finite_subcover }

/-- A set `s` is compact if and only if
for every family of closed sets whose intersection avoids `s`,
there exists a finite subfamily whose intersection avoids `s`. -/
theorem compact_iff_finite_subfamily_closed {α : Type u} [topological_space α] {s : set α} :
    is_compact s ↔
        ∀ {ι : Type u} (Z : ι → set α),
          (∀ (i : ι), is_closed (Z i)) →
            (s ∩ set.Inter fun (i : ι) => Z i) = ∅ →
              ∃ (t : finset ι),
                (s ∩ set.Inter fun (i : ι) => set.Inter fun (H : i ∈ t) => Z i) = ∅ :=
  { mp := fun (hs : is_compact s) (ι : Type u) => is_compact.elim_finite_subfamily_closed hs,
    mpr := compact_of_finite_subfamily_closed }

@[simp] theorem compact_empty {α : Type u} [topological_space α] : is_compact ∅ :=
  fun (f : filter α) (hnf : filter.ne_bot f) (hsf : f ≤ filter.principal ∅) =>
    not.elim hnf (iff.mp filter.empty_in_sets_eq_bot (iff.mp filter.le_principal_iff hsf))

@[simp] theorem compact_singleton {α : Type u} [topological_space α] {a : α} :
    is_compact (singleton a) :=
  sorry

theorem set.subsingleton.is_compact {α : Type u} [topological_space α] {s : set α}
    (hs : set.subsingleton s) : is_compact s :=
  set.subsingleton.induction_on hs compact_empty fun (x : α) => compact_singleton

theorem set.finite.compact_bUnion {α : Type u} {β : Type v} [topological_space α] {s : set β}
    {f : β → set α} (hs : set.finite s) (hf : ∀ (i : β), i ∈ s → is_compact (f i)) :
    is_compact (set.Union fun (i : β) => set.Union fun (H : i ∈ s) => f i) :=
  sorry

theorem compact_Union {α : Type u} {β : Type v} [topological_space α] {f : β → set α} [fintype β]
    (h : ∀ (i : β), is_compact (f i)) : is_compact (set.Union fun (i : β) => f i) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (is_compact (set.Union fun (i : β) => f i)))
        (Eq.symm (set.bUnion_univ fun (i : β) => f i))))
    (set.finite.compact_bUnion set.finite_univ fun (i : β) (_x : i ∈ set.univ) => h i)

theorem set.finite.is_compact {α : Type u} [topological_space α] {s : set α} (hs : set.finite s) :
    is_compact s :=
  set.bUnion_of_singleton s ▸
    set.finite.compact_bUnion hs fun (_x : α) (_x_1 : _x ∈ s) => compact_singleton

theorem is_compact.union {α : Type u} [topological_space α] {s : set α} {t : set α}
    (hs : is_compact s) (ht : is_compact t) : is_compact (s ∪ t) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (is_compact (s ∪ t))) set.union_eq_Union))
    (compact_Union fun (b : Bool) => bool.cases_on b ht hs)

theorem is_compact.insert {α : Type u} [topological_space α] {s : set α} (hs : is_compact s)
    (a : α) : is_compact (insert a s) :=
  is_compact.union compact_singleton hs

/-- `filter.cocompact` is the filter generated by complements to compact sets. -/
def filter.cocompact (α : Type u_1) [topological_space α] : filter α :=
  infi fun (s : set α) => infi fun (hs : is_compact s) => filter.principal (sᶜ)

theorem filter.has_basis_cocompact {α : Type u} [topological_space α] :
    filter.has_basis (filter.cocompact α) is_compact compl :=
  sorry

theorem filter.mem_cocompact {α : Type u} [topological_space α] {s : set α} :
    s ∈ filter.cocompact α ↔ ∃ (t : set α), is_compact t ∧ tᶜ ⊆ s :=
  iff.trans (filter.has_basis.mem_iff filter.has_basis_cocompact)
    (exists_congr fun (t : set α) => exists_prop)

theorem filter.mem_cocompact' {α : Type u} [topological_space α] {s : set α} :
    s ∈ filter.cocompact α ↔ ∃ (t : set α), is_compact t ∧ sᶜ ⊆ t :=
  iff.trans filter.mem_cocompact
    (exists_congr
      fun (t : set α) => and_congr_right fun (ht : is_compact t) => set.compl_subset_comm)

theorem is_compact.compl_mem_cocompact {α : Type u} [topological_space α] {s : set α}
    (hs : is_compact s) : sᶜ ∈ filter.cocompact α :=
  filter.has_basis.mem_of_mem filter.has_basis_cocompact hs

/-- `nhds_contain_boxes s t` means that any open neighborhood of `s × t` in `α × β` includes
a product of an open neighborhood of `s` by an open neighborhood of `t`. -/
def nhds_contain_boxes {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    (s : set α) (t : set β) :=
  ∀ (n : set (α × β)),
    is_open n →
      set.prod s t ⊆ n →
        ∃ (u : set α), ∃ (v : set β), is_open u ∧ is_open v ∧ s ⊆ u ∧ t ⊆ v ∧ set.prod u v ⊆ n

theorem nhds_contain_boxes.symm {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] {s : set α} {t : set β} :
    nhds_contain_boxes s t → nhds_contain_boxes t s :=
  sorry

theorem nhds_contain_boxes.comm {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] {s : set α} {t : set β} :
    nhds_contain_boxes s t ↔ nhds_contain_boxes t s :=
  { mp := nhds_contain_boxes.symm, mpr := nhds_contain_boxes.symm }

theorem nhds_contain_boxes_of_singleton {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] {x : α} {y : β} : nhds_contain_boxes (singleton x) (singleton y) :=
  sorry

theorem nhds_contain_boxes_of_compact {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] {s : set α} (hs : is_compact s) (t : set β)
    (H : ∀ (x : α), x ∈ s → nhds_contain_boxes (singleton x) t) : nhds_contain_boxes s t :=
  sorry

/-- If `s` and `t` are compact sets and `n` is an open neighborhood of `s × t`, then there exist
open neighborhoods `u ⊇ s` and `v ⊇ t` such that `u × v ⊆ n`. -/
theorem generalized_tube_lemma {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    {s : set α} (hs : is_compact s) {t : set β} (ht : is_compact t) {n : set (α × β)}
    (hn : is_open n) (hp : set.prod s t ⊆ n) :
    ∃ (u : set α), ∃ (v : set β), is_open u ∧ is_open v ∧ s ⊆ u ∧ t ⊆ v ∧ set.prod u v ⊆ n :=
  sorry

/-- Type class for compact spaces. Separation is sometimes included in the definition, especially
in the French literature, but we do not include it here. -/
class compact_space (α : Type u_1) [topological_space α] where
  compact_univ : is_compact set.univ

protected instance subsingleton.compact_space {α : Type u} [topological_space α] [subsingleton α] :
    compact_space α :=
  compact_space.mk (set.subsingleton.is_compact set.subsingleton_univ)

theorem compact_univ {α : Type u} [topological_space α] [h : compact_space α] :
    is_compact set.univ :=
  compact_space.compact_univ

theorem cluster_point_of_compact {α : Type u} [topological_space α] [compact_space α] (f : filter α)
    [filter.ne_bot f] : ∃ (x : α), cluster_pt x f :=
  sorry

theorem compact_space_of_finite_subfamily_closed {α : Type u} [topological_space α]
    (h :
      ∀ {ι : Type u} (Z : ι → set α),
        (∀ (i : ι), is_closed (Z i)) →
          (set.Inter fun (i : ι) => Z i) = ∅ →
            ∃ (t : finset ι), (set.Inter fun (i : ι) => set.Inter fun (H : i ∈ t) => Z i) = ∅) :
    compact_space α :=
  sorry

theorem is_closed.compact {α : Type u} [topological_space α] [compact_space α] {s : set α}
    (h : is_closed s) : is_compact s :=
  compact_of_is_closed_subset compact_univ h (set.subset_univ s)

theorem is_compact.image_of_continuous_on {α : Type u} {β : Type v} [topological_space α]
    {s : set α} [topological_space β] {f : α → β} (hs : is_compact s) (hf : continuous_on f s) :
    is_compact (f '' s) :=
  sorry

theorem is_compact.image {α : Type u} {β : Type v} [topological_space α] {s : set α}
    [topological_space β] {f : α → β} (hs : is_compact s) (hf : continuous f) :
    is_compact (f '' s) :=
  is_compact.image_of_continuous_on hs (continuous.continuous_on hf)

theorem compact_range {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    [compact_space α] {f : α → β} (hf : continuous f) : is_compact (set.range f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (is_compact (set.range f))) (Eq.symm set.image_univ)))
    (is_compact.image compact_univ hf)

/-- If X is is_compact then pr₂ : X × Y → Y is a closed map -/
theorem is_closed_proj_of_compact {X : Type u_1} [topological_space X] [compact_space X]
    {Y : Type u_2} [topological_space Y] : is_closed_map prod.snd :=
  sorry

theorem embedding.compact_iff_compact_image {α : Type u} {β : Type v} [topological_space α]
    {s : set α} [topological_space β] {f : α → β} (hf : embedding f) :
    is_compact s ↔ is_compact (f '' s) :=
  sorry

theorem compact_iff_compact_in_subtype {α : Type u} [topological_space α] {p : α → Prop}
    {s : set (Subtype fun (a : α) => p a)} : is_compact s ↔ is_compact (coe '' s) :=
  embedding.compact_iff_compact_image embedding_subtype_coe

theorem compact_iff_compact_univ {α : Type u} [topological_space α] {s : set α} :
    is_compact s ↔ is_compact set.univ :=
  sorry

theorem compact_iff_compact_space {α : Type u} [topological_space α] {s : set α} :
    is_compact s ↔ compact_space ↥s :=
  iff.trans compact_iff_compact_univ
    { mp := fun (h : is_compact set.univ) => compact_space.mk h, mpr := compact_space.compact_univ }

theorem is_compact.prod {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    {s : set α} {t : set β} (hs : is_compact s) (ht : is_compact t) : is_compact (set.prod s t) :=
  sorry

/-- Finite topological spaces are compact. -/
protected instance fintype.compact_space {α : Type u} [topological_space α] [fintype α] :
    compact_space α :=
  compact_space.mk (set.finite.is_compact set.finite_univ)

/-- The product of two compact spaces is compact. -/
protected instance prod.compact_space {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] [compact_space α] [compact_space β] : compact_space (α × β) :=
  compact_space.mk
    (eq.mpr (id (Eq._oldrec (Eq.refl (is_compact set.univ)) (Eq.symm set.univ_prod_univ)))
      (is_compact.prod compact_univ compact_univ))

/-- The disjoint union of two compact spaces is compact. -/
protected instance sum.compact_space {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] [compact_space α] [compact_space β] : compact_space (α ⊕ β) :=
  compact_space.mk
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (is_compact set.univ)) (Eq.symm set.range_inl_union_range_inr)))
      (is_compact.union (compact_range continuous_inl) (compact_range continuous_inr)))

/-- Tychonoff's theorem -/
theorem compact_pi_infinite {ι : Type u_1} {π : ι → Type u_2} [(i : ι) → topological_space (π i)]
    {s : (i : ι) → set (π i)} :
    (∀ (i : ι), is_compact (s i)) →
        is_compact (set_of fun (x : (i : ι) → π i) => ∀ (i : ι), x i ∈ s i) :=
  sorry

/-- A version of Tychonoff's theorem that uses `set.pi`. -/
theorem compact_univ_pi {ι : Type u_1} {π : ι → Type u_2} [(i : ι) → topological_space (π i)]
    {s : (i : ι) → set (π i)} (h : ∀ (i : ι), is_compact (s i)) : is_compact (set.pi set.univ s) :=
  sorry

protected instance pi.compact {ι : Type u_1} {π : ι → Type u_2} [(i : ι) → topological_space (π i)]
    [∀ (i : ι), compact_space (π i)] : compact_space ((i : ι) → π i) :=
  sorry

protected instance quot.compact_space {α : Type u} [topological_space α] {r : α → α → Prop}
    [compact_space α] : compact_space (Quot r) :=
  compact_space.mk
    (eq.mpr (id (Eq._oldrec (Eq.refl (is_compact set.univ)) (Eq.symm (set.range_quot_mk r))))
      (compact_range continuous_quot_mk))

protected instance quotient.compact_space {α : Type u} [topological_space α] {s : setoid α}
    [compact_space α] : compact_space (quotient s) :=
  quot.compact_space

/-- There are various definitions of "locally compact space" in the literature, which agree for
Hausdorff spaces but not in general. This one is the precise condition on X needed for the
evaluation `map C(X, Y) × X → Y` to be continuous for all `Y` when `C(X, Y)` is given the
compact-open topology. -/
class locally_compact_space (α : Type u_1) [topological_space α] where
  local_compact_nhds :
    ∀ (x : α) (n : set α) (H : n ∈ nhds x), ∃ (s : set α), ∃ (H : s ∈ nhds x), s ⊆ n ∧ is_compact s

/-- A reformulation of the definition of locally compact space: In a locally compact space,
  every open set containing `x` has a compact subset containing `x` in its interior. -/
theorem exists_compact_subset {α : Type u} [topological_space α] [locally_compact_space α] {x : α}
    {U : set α} (hU : is_open U) (hx : x ∈ U) :
    ∃ (K : set α), is_compact K ∧ x ∈ interior K ∧ K ⊆ U :=
  sorry

/-- In a locally compact space every point has a compact neighborhood. -/
theorem exists_compact_mem_nhds {α : Type u} [topological_space α] [locally_compact_space α]
    (x : α) : ∃ (K : set α), is_compact K ∧ K ∈ nhds x :=
  sorry

theorem ultrafilter.le_nhds_Lim {α : Type u} [topological_space α] [compact_space α]
    (F : ultrafilter α) : ↑F ≤ nhds (Lim ↑F) :=
  sorry

/-- A σ-compact space is a space that is the union of a countable collection of compact subspaces.
  Note that a locally compact separable T₂ space need not be σ-compact.
  The sequence can be extracted using `topological_space.compact_covering`. -/
class sigma_compact_space (α : Type u_1) [topological_space α] where
  exists_compact_covering :
    ∃ (K : ℕ → set α), (∀ (n : ℕ), is_compact (K n)) ∧ (set.Union fun (n : ℕ) => K n) = set.univ

protected instance compact_space.sigma_compact {α : Type u} [topological_space α]
    [compact_space α] : sigma_compact_space α :=
  sigma_compact_space.mk
    (Exists.intro (fun (_x : ℕ) => set.univ)
      { left := fun (_x : ℕ) => compact_univ, right := set.Union_const set.univ })

theorem sigma_compact_space.of_countable {α : Type u} [topological_space α] (S : set (set α))
    (Hc : set.countable S) (Hcomp : ∀ (s : set α), s ∈ S → is_compact s) (HU : ⋃₀S = set.univ) :
    sigma_compact_space α :=
  sigma_compact_space.mk
    (iff.mpr (set.exists_seq_cover_iff_countable (Exists.intro ∅ compact_empty))
      (Exists.intro S { left := Hc, right := { left := Hcomp, right := HU } }))

theorem sigma_compact_space_of_locally_compact_second_countable {α : Type u} [topological_space α]
    [locally_compact_space α] [topological_space.second_countable_topology α] :
    sigma_compact_space α :=
  sorry

/-- An arbitrary compact covering of a σ-compact space. -/
def compact_covering (α : Type u) [topological_space α] [sigma_compact_space α] : ℕ → set α :=
  classical.some sigma_compact_space.exists_compact_covering

theorem is_compact_compact_covering (α : Type u) [topological_space α] [sigma_compact_space α]
    (n : ℕ) : is_compact (compact_covering α n) :=
  and.left (classical.some_spec sigma_compact_space.exists_compact_covering) n

theorem Union_compact_covering (α : Type u) [topological_space α] [sigma_compact_space α] :
    (set.Union fun (n : ℕ) => compact_covering α n) = set.univ :=
  and.right (classical.some_spec sigma_compact_space.exists_compact_covering)

/-- A set is clopen if it is both open and closed. -/
def is_clopen {α : Type u} [topological_space α] (s : set α) := is_open s ∧ is_closed s

theorem is_clopen_union {α : Type u} [topological_space α] {s : set α} {t : set α}
    (hs : is_clopen s) (ht : is_clopen t) : is_clopen (s ∪ t) :=
  { left := is_open_union (and.left hs) (and.left ht),
    right := is_closed_union (and.right hs) (and.right ht) }

theorem is_clopen_inter {α : Type u} [topological_space α] {s : set α} {t : set α}
    (hs : is_clopen s) (ht : is_clopen t) : is_clopen (s ∩ t) :=
  { left := is_open_inter (and.left hs) (and.left ht),
    right := is_closed_inter (and.right hs) (and.right ht) }

@[simp] theorem is_clopen_empty {α : Type u} [topological_space α] : is_clopen ∅ :=
  { left := is_open_empty, right := is_closed_empty }

@[simp] theorem is_clopen_univ {α : Type u} [topological_space α] : is_clopen set.univ :=
  { left := is_open_univ, right := is_closed_univ }

theorem is_clopen_compl {α : Type u} [topological_space α] {s : set α} (hs : is_clopen s) :
    is_clopen (sᶜ) :=
  { left := and.right hs, right := iff.mpr is_closed_compl_iff (and.left hs) }

@[simp] theorem is_clopen_compl_iff {α : Type u} [topological_space α] {s : set α} :
    is_clopen (sᶜ) ↔ is_clopen s :=
  { mp := fun (h : is_clopen (sᶜ)) => compl_compl s ▸ is_clopen_compl h, mpr := is_clopen_compl }

theorem is_clopen_diff {α : Type u} [topological_space α] {s : set α} {t : set α} (hs : is_clopen s)
    (ht : is_clopen t) : is_clopen (s \ t) :=
  is_clopen_inter hs (is_clopen_compl ht)

theorem is_clopen_Inter {α : Type u} [topological_space α] {β : Type u_1} [fintype β]
    {s : β → set α} (h : ∀ (i : β), is_clopen (s i)) : is_clopen (set.Inter fun (i : β) => s i) :=
  { left := is_open_Inter (and.left (iff.mp forall_and_distrib h)),
    right := is_closed_Inter (and.right (iff.mp forall_and_distrib h)) }

theorem is_clopen_bInter {α : Type u} [topological_space α] {β : Type u_1} {s : finset β}
    {f : β → set α} (h : ∀ (i : β), i ∈ s → is_clopen (f i)) :
    is_clopen (set.Inter fun (i : β) => set.Inter fun (H : i ∈ s) => f i) :=
  sorry

theorem continuous_on.preimage_clopen_of_clopen {α : Type u} [topological_space α] {β : Type u_1}
    [topological_space β] {f : α → β} {s : set α} {t : set β} (hf : continuous_on f s)
    (hs : is_clopen s) (ht : is_clopen t) : is_clopen (s ∩ f ⁻¹' t) :=
  { left := continuous_on.preimage_open_of_open hf (and.left hs) (and.left ht),
    right := continuous_on.preimage_closed_of_closed hf (and.right hs) (and.right ht) }

/-- The intersection of a disjoint covering by two open sets of a clopen set will be clopen. -/
theorem is_clopen_inter_of_disjoint_cover_clopen {α : Type u} [topological_space α] {Z : set α}
    {a : set α} {b : set α} (h : is_clopen Z) (cover : Z ⊆ a ∪ b) (ha : is_open a) (hb : is_open b)
    (hab : a ∩ b = ∅) : is_clopen (Z ∩ a) :=
  sorry

/-- A preirreducible set `s` is one where there is no non-trivial pair of disjoint opens on `s`. -/
def is_preirreducible {α : Type u} [topological_space α] (s : set α) :=
  ∀ (u v : set α),
    is_open u → is_open v → set.nonempty (s ∩ u) → set.nonempty (s ∩ v) → set.nonempty (s ∩ (u ∩ v))

/-- An irreducible set `s` is one that is nonempty and
where there is no non-trivial pair of disjoint opens on `s`. -/
def is_irreducible {α : Type u} [topological_space α] (s : set α) :=
  set.nonempty s ∧ is_preirreducible s

theorem is_irreducible.nonempty {α : Type u} [topological_space α] {s : set α}
    (h : is_irreducible s) : set.nonempty s :=
  and.left h

theorem is_irreducible.is_preirreducible {α : Type u} [topological_space α] {s : set α}
    (h : is_irreducible s) : is_preirreducible s :=
  and.right h

theorem is_preirreducible_empty {α : Type u} [topological_space α] : is_preirreducible ∅ := sorry

theorem is_irreducible_singleton {α : Type u} [topological_space α] {x : α} :
    is_irreducible (singleton x) :=
  sorry

theorem is_preirreducible.closure {α : Type u} [topological_space α] {s : set α}
    (H : is_preirreducible s) : is_preirreducible (closure s) :=
  sorry

theorem is_irreducible.closure {α : Type u} [topological_space α] {s : set α}
    (h : is_irreducible s) : is_irreducible (closure s) :=
  { left := set.nonempty.closure (is_irreducible.nonempty h),
    right := is_preirreducible.closure (is_irreducible.is_preirreducible h) }

theorem exists_preirreducible {α : Type u} [topological_space α] (s : set α)
    (H : is_preirreducible s) :
    ∃ (t : set α),
        is_preirreducible t ∧ s ⊆ t ∧ ∀ (u : set α), is_preirreducible u → t ⊆ u → u = t :=
  sorry

/-- A maximal irreducible set that contains a given point. -/
def irreducible_component {α : Type u} [topological_space α] (x : α) : set α := classical.some sorry

theorem irreducible_component_property {α : Type u} [topological_space α] (x : α) :
    is_preirreducible (irreducible_component x) ∧
        singleton x ⊆ irreducible_component x ∧
          ∀ (u : set α),
            is_preirreducible u → irreducible_component x ⊆ u → u = irreducible_component x :=
  classical.some_spec
    (exists_preirreducible (singleton x)
      (is_irreducible.is_preirreducible is_irreducible_singleton))

theorem mem_irreducible_component {α : Type u} [topological_space α] {x : α} :
    x ∈ irreducible_component x :=
  iff.mp set.singleton_subset_iff (and.left (and.right (irreducible_component_property x)))

theorem is_irreducible_irreducible_component {α : Type u} [topological_space α] {x : α} :
    is_irreducible (irreducible_component x) :=
  { left := Exists.intro x mem_irreducible_component,
    right := and.left (irreducible_component_property x) }

theorem eq_irreducible_component {α : Type u} [topological_space α] {x : α} {s : set α} :
    is_preirreducible s → irreducible_component x ⊆ s → s = irreducible_component x :=
  and.right (and.right (irreducible_component_property x))

theorem is_closed_irreducible_component {α : Type u} [topological_space α] {x : α} :
    is_closed (irreducible_component x) :=
  iff.mp closure_eq_iff_is_closed
    (eq_irreducible_component
      (is_preirreducible.closure
        (is_irreducible.is_preirreducible is_irreducible_irreducible_component))
      subset_closure)

/-- A preirreducible space is one where there is no non-trivial pair of disjoint opens. -/
class preirreducible_space (α : Type u) [topological_space α] where
  is_preirreducible_univ : is_preirreducible set.univ

/-- An irreducible space is one that is nonempty
and where there is no non-trivial pair of disjoint opens. -/
class irreducible_space (α : Type u) [topological_space α]
    extends Nonempty α, preirreducible_space α where
  to_nonempty : Nonempty α

theorem nonempty_preirreducible_inter {α : Type u} [topological_space α] [preirreducible_space α]
    {s : set α} {t : set α} :
    is_open s → is_open t → set.nonempty s → set.nonempty t → set.nonempty (s ∩ t) :=
  sorry

theorem is_preirreducible.image {α : Type u} {β : Type v} [topological_space α]
    [topological_space β] {s : set α} (H : is_preirreducible s) (f : α → β)
    (hf : continuous_on f s) : is_preirreducible (f '' s) :=
  sorry

theorem is_irreducible.image {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    {s : set α} (H : is_irreducible s) (f : α → β) (hf : continuous_on f s) :
    is_irreducible (f '' s) :=
  { left := iff.mpr set.nonempty_image_iff (is_irreducible.nonempty H),
    right := is_preirreducible.image (is_irreducible.is_preirreducible H) f hf }

theorem subtype.preirreducible_space {α : Type u} [topological_space α] {s : set α}
    (h : is_preirreducible s) : preirreducible_space ↥s :=
  sorry

theorem subtype.irreducible_space {α : Type u} [topological_space α] {s : set α}
    (h : is_irreducible s) : irreducible_space ↥s :=
  irreducible_space.mk (set.nonempty.to_subtype (is_irreducible.nonempty h))

/-- A set `s` is irreducible if and only if
for every finite collection of open sets all of whose members intersect `s`,
`s` also intersects the intersection of the entire collection
(i.e., there is an element of `s` contained in every member of the collection). -/
theorem is_irreducible_iff_sInter {α : Type u} [topological_space α] {s : set α} :
    is_irreducible s ↔
        ∀ (U : finset (set α)),
          (∀ (u : set α), u ∈ U → is_open u) →
            (∀ (u : set α), u ∈ U → set.nonempty (s ∩ u)) → set.nonempty (s ∩ ⋂₀↑U) :=
  sorry

/-- A set is preirreducible if and only if
for every cover by two closed sets, it is contained in one of the two covering sets. -/
theorem is_preirreducible_iff_closed_union_closed {α : Type u} [topological_space α] {s : set α} :
    is_preirreducible s ↔
        ∀ (z₁ z₂ : set α), is_closed z₁ → is_closed z₂ → s ⊆ z₁ ∪ z₂ → s ⊆ z₁ ∨ s ⊆ z₂ :=
  sorry

/-- A set is irreducible if and only if
for every cover by a finite collection of closed sets,
it is contained in one of the members of the collection. -/
theorem is_irreducible_iff_sUnion_closed {α : Type u} [topological_space α] {s : set α} :
    is_irreducible s ↔
        ∀ (Z : finset (set α)),
          (∀ (z : set α), z ∈ Z → is_closed z) →
            ∀ (H : s ⊆ ⋃₀↑Z), ∃ (z : set α), ∃ (H : z ∈ Z), s ⊆ z :=
  sorry

/-- A preconnected set is one where there is no non-trivial open partition. -/
def is_preconnected {α : Type u} [topological_space α] (s : set α) :=
  ∀ (u v : set α),
    is_open u →
      is_open v →
        s ⊆ u ∪ v → set.nonempty (s ∩ u) → set.nonempty (s ∩ v) → set.nonempty (s ∩ (u ∩ v))

/-- A connected set is one that is nonempty and where there is no non-trivial open partition. -/
def is_connected {α : Type u} [topological_space α] (s : set α) :=
  set.nonempty s ∧ is_preconnected s

theorem is_connected.nonempty {α : Type u} [topological_space α] {s : set α} (h : is_connected s) :
    set.nonempty s :=
  and.left h

theorem is_connected.is_preconnected {α : Type u} [topological_space α] {s : set α}
    (h : is_connected s) : is_preconnected s :=
  and.right h

theorem is_preirreducible.is_preconnected {α : Type u} [topological_space α] {s : set α}
    (H : is_preirreducible s) : is_preconnected s :=
  fun (_x _x_1 : set α) (hu : is_open _x) (hv : is_open _x_1) (_x_2 : s ⊆ _x ∪ _x_1) =>
    H _x _x_1 hu hv

theorem is_irreducible.is_connected {α : Type u} [topological_space α] {s : set α}
    (H : is_irreducible s) : is_connected s :=
  { left := is_irreducible.nonempty H,
    right := is_preirreducible.is_preconnected (is_irreducible.is_preirreducible H) }

theorem is_preconnected_empty {α : Type u} [topological_space α] : is_preconnected ∅ :=
  is_preirreducible.is_preconnected is_preirreducible_empty

theorem is_connected_singleton {α : Type u} [topological_space α] {x : α} :
    is_connected (singleton x) :=
  is_irreducible.is_connected is_irreducible_singleton

/-- If any point of a set is joined to a fixed point by a preconnected subset,
then the original set is preconnected as well. -/
theorem is_preconnected_of_forall {α : Type u} [topological_space α] {s : set α} (x : α)
    (H : ∀ (y : α) (H : y ∈ s), ∃ (t : set α), ∃ (H : t ⊆ s), x ∈ t ∧ y ∈ t ∧ is_preconnected t) :
    is_preconnected s :=
  sorry

/-- If any two points of a set are contained in a preconnected subset,
then the original set is preconnected as well. -/
theorem is_preconnected_of_forall_pair {α : Type u} [topological_space α] {s : set α}
    (H :
      ∀ (x y : α) (H : x ∈ s) (H : y ∈ s),
        ∃ (t : set α), ∃ (H : t ⊆ s), x ∈ t ∧ y ∈ t ∧ is_preconnected t) :
    is_preconnected s :=
  sorry

/-- A union of a family of preconnected sets with a common point is preconnected as well. -/
theorem is_preconnected_sUnion {α : Type u} [topological_space α] (x : α) (c : set (set α))
    (H1 : ∀ (s : set α), s ∈ c → x ∈ s) (H2 : ∀ (s : set α), s ∈ c → is_preconnected s) :
    is_preconnected (⋃₀c) :=
  sorry

theorem is_preconnected.union {α : Type u} [topological_space α] (x : α) {s : set α} {t : set α}
    (H1 : x ∈ s) (H2 : x ∈ t) (H3 : is_preconnected s) (H4 : is_preconnected t) :
    is_preconnected (s ∪ t) :=
  sorry

theorem is_connected.union {α : Type u} [topological_space α] {s : set α} {t : set α}
    (H : set.nonempty (s ∩ t)) (Hs : is_connected s) (Ht : is_connected t) : is_connected (s ∪ t) :=
  sorry

theorem is_preconnected.closure {α : Type u} [topological_space α] {s : set α}
    (H : is_preconnected s) : is_preconnected (closure s) :=
  sorry

theorem is_connected.closure {α : Type u} [topological_space α] {s : set α} (H : is_connected s) :
    is_connected (closure s) :=
  { left := set.nonempty.closure (is_connected.nonempty H),
    right := is_preconnected.closure (is_connected.is_preconnected H) }

theorem is_preconnected.image {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    {s : set α} (H : is_preconnected s) (f : α → β) (hf : continuous_on f s) :
    is_preconnected (f '' s) :=
  sorry

theorem is_connected.image {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    {s : set α} (H : is_connected s) (f : α → β) (hf : continuous_on f s) : is_connected (f '' s) :=
  { left := iff.mpr set.nonempty_image_iff (is_connected.nonempty H),
    right := is_preconnected.image (is_connected.is_preconnected H) f hf }

theorem is_preconnected_closed_iff {α : Type u} [topological_space α] {s : set α} :
    is_preconnected s ↔
        ∀ (t t' : set α),
          is_closed t →
            is_closed t' →
              s ⊆ t ∪ t' →
                set.nonempty (s ∩ t) → set.nonempty (s ∩ t') → set.nonempty (s ∩ (t ∩ t')) :=
  sorry

/-- The connected component of a point is the maximal connected set
that contains this point. -/
def connected_component {α : Type u} [topological_space α] (x : α) : set α :=
  ⋃₀set_of fun (s : set α) => is_preconnected s ∧ x ∈ s

/-- The connected component of a point inside a set. -/
def connected_component_in {α : Type u} [topological_space α] (F : set α) (x : ↥F) : set α :=
  coe '' connected_component x

theorem mem_connected_component {α : Type u} [topological_space α] {x : α} :
    x ∈ connected_component x :=
  set.mem_sUnion_of_mem (set.mem_singleton x)
    { left := is_connected.is_preconnected is_connected_singleton, right := set.mem_singleton x }

theorem is_connected_connected_component {α : Type u} [topological_space α] {x : α} :
    is_connected (connected_component x) :=
  sorry

theorem subset_connected_component {α : Type u} [topological_space α] {x : α} {s : set α}
    (H1 : is_preconnected s) (H2 : x ∈ s) : s ⊆ connected_component x :=
  fun (z : α) (hz : z ∈ s) => set.mem_sUnion_of_mem hz { left := H1, right := H2 }

theorem is_closed_connected_component {α : Type u} [topological_space α] {x : α} :
    is_closed (connected_component x) :=
  sorry

theorem irreducible_component_subset_connected_component {α : Type u} [topological_space α]
    {x : α} : irreducible_component x ⊆ connected_component x :=
  subset_connected_component
    (is_connected.is_preconnected
      (is_irreducible.is_connected is_irreducible_irreducible_component))
    mem_irreducible_component

/-- A preconnected space is one where there is no non-trivial open partition. -/
class preconnected_space (α : Type u) [topological_space α] where
  is_preconnected_univ : is_preconnected set.univ

/-- A connected space is a nonempty one where there is no non-trivial open partition. -/
class connected_space (α : Type u) [topological_space α] extends preconnected_space α, Nonempty α
    where
  to_nonempty : Nonempty α

theorem is_connected_range {α : Type u} {β : Type v} [topological_space α] [topological_space β]
    [connected_space α] {f : α → β} (h : continuous f) : is_connected (set.range f) :=
  sorry

theorem connected_space_iff_connected_component {α : Type u} [topological_space α] :
    connected_space α ↔ ∃ (x : α), connected_component x = set.univ :=
  sorry

protected instance preirreducible_space.preconnected_space (α : Type u) [topological_space α]
    [preirreducible_space α] : preconnected_space α :=
  preconnected_space.mk
    (is_preirreducible.is_preconnected (preirreducible_space.is_preirreducible_univ α))

protected instance irreducible_space.connected_space (α : Type u) [topological_space α]
    [irreducible_space α] : connected_space α :=
  connected_space.mk (irreducible_space.to_nonempty α)

theorem nonempty_inter {α : Type u} [topological_space α] [preconnected_space α] {s : set α}
    {t : set α} :
    is_open s →
        is_open t → s ∪ t = set.univ → set.nonempty s → set.nonempty t → set.nonempty (s ∩ t) :=
  sorry

theorem is_clopen_iff {α : Type u} [topological_space α] [preconnected_space α] {s : set α} :
    is_clopen s ↔ s = ∅ ∨ s = set.univ :=
  sorry

theorem eq_univ_of_nonempty_clopen {α : Type u} [topological_space α] [preconnected_space α]
    {s : set α} (h : set.nonempty s) (h' : is_clopen s) : s = set.univ :=
  sorry

theorem subtype.preconnected_space {α : Type u} [topological_space α] {s : set α}
    (h : is_preconnected s) : preconnected_space ↥s :=
  sorry

theorem subtype.connected_space {α : Type u} [topological_space α] {s : set α}
    (h : is_connected s) : connected_space ↥s :=
  connected_space.mk (set.nonempty.to_subtype (is_connected.nonempty h))

theorem is_preconnected_iff_preconnected_space {α : Type u} [topological_space α] {s : set α} :
    is_preconnected s ↔ preconnected_space ↥s :=
  sorry

theorem is_connected_iff_connected_space {α : Type u} [topological_space α] {s : set α} :
    is_connected s ↔ connected_space ↥s :=
  sorry

/-- A set `s` is preconnected if and only if
for every cover by two open sets that are disjoint on `s`,
it is contained in one of the two covering sets. -/
theorem is_preconnected_iff_subset_of_disjoint {α : Type u} [topological_space α] {s : set α} :
    is_preconnected s ↔
        ∀ (u v : set α), is_open u → is_open v → s ⊆ u ∪ v → s ∩ (u ∩ v) = ∅ → s ⊆ u ∨ s ⊆ v :=
  sorry

/-- A set `s` is connected if and only if
for every cover by a finite collection of open sets that are pairwise disjoint on `s`,
it is contained in one of the members of the collection. -/
theorem is_connected_iff_sUnion_disjoint_open {α : Type u} [topological_space α] {s : set α} :
    is_connected s ↔
        ∀ (U : finset (set α))
          (H : ∀ (u v : set α), u ∈ U → v ∈ U → set.nonempty (s ∩ (u ∩ v)) → u = v),
          (∀ (u : set α), u ∈ U → is_open u) → s ⊆ ⋃₀↑U → ∃ (u : set α), ∃ (H : u ∈ U), s ⊆ u :=
  sorry

/-- Preconnected sets are either contained in or disjoint to any given clopen set. -/
theorem subset_or_disjoint_of_clopen {α : Type u_1} [topological_space α] {s : set α} {t : set α}
    (h : is_preconnected t) (h1 : is_clopen s) : s ∩ t = ∅ ∨ t ⊆ s :=
  sorry

/-- A set `s` is preconnected if and only if
for every cover by two closed sets that are disjoint on `s`,
it is contained in one of the two covering sets. -/
theorem is_preconnected_iff_subset_of_disjoint_closed {α : Type u_1} {s : set α}
    [topological_space α] :
    is_preconnected s ↔
        ∀ (u v : set α), is_closed u → is_closed v → s ⊆ u ∪ v → s ∩ (u ∩ v) = ∅ → s ⊆ u ∨ s ⊆ v :=
  sorry

/-- A closed set `s` is preconnected if and only if
for every cover by two closed sets that are disjoint,
it is contained in one of the two covering sets. -/
theorem is_preconnected_iff_subset_of_fully_disjoint_closed {α : Type u} [topological_space α]
    {s : set α} (hs : is_closed s) :
    is_preconnected s ↔
        ∀ (u v : set α), is_closed u → is_closed v → s ⊆ u ∪ v → u ∩ v = ∅ → s ⊆ u ∨ s ⊆ v :=
  sorry

/-- The connected component of a point is always a subset of the intersection of all its clopen
neighbourhoods. -/
theorem connected_component_subset_Inter_clopen {α : Type u} [topological_space α] {x : α} :
    connected_component x ⊆
        set.Inter fun (Z : Subtype fun (Z : set α) => is_clopen Z ∧ x ∈ Z) => ↑Z :=
  sorry

/-- A set is called totally disconnected if all of its connected components are singletons. -/
def is_totally_disconnected {α : Type u} [topological_space α] (s : set α) :=
  ∀ (t : set α), t ⊆ s → is_preconnected t → subsingleton ↥t

theorem is_totally_disconnected_empty {α : Type u} [topological_space α] :
    is_totally_disconnected ∅ :=
  sorry

theorem is_totally_disconnected_singleton {α : Type u} [topological_space α] {x : α} :
    is_totally_disconnected (singleton x) :=
  sorry

/-- A space is totally disconnected if all of its connected components are singletons. -/
class totally_disconnected_space (α : Type u) [topological_space α] where
  is_totally_disconnected_univ : is_totally_disconnected set.univ

protected instance pi.totally_disconnected_space {α : Type u_1} {β : α → Type u_2}
    [t₂ : (a : α) → topological_space (β a)] [∀ (a : α), totally_disconnected_space (β a)] :
    totally_disconnected_space ((a : α) → β a) :=
  sorry

protected instance subtype.totally_disconnected_space {α : Type u_1} {p : α → Prop}
    [topological_space α] [totally_disconnected_space α] : totally_disconnected_space (Subtype p) :=
  totally_disconnected_space.mk
    fun (s : set (Subtype p)) (h1 : s ⊆ set.univ) (h2 : is_preconnected s) =>
      set.subsingleton_of_image subtype.val_injective s
        (totally_disconnected_space.is_totally_disconnected_univ (subtype.val '' s)
          (set.subset_univ (subtype.val '' s))
          (is_preconnected.image h2 subtype.val (continuous.continuous_on continuous_subtype_val)))

/-- A set `s` is called totally separated if any two points of this set can be separated
by two disjoint open sets covering `s`. -/
def is_totally_separated {α : Type u} [topological_space α] (s : set α) :=
  ∀ (x : α),
    x ∈ s →
      ∀ (y : α),
        y ∈ s →
          x ≠ y →
            ∃ (u : set α),
              ∃ (v : set α), is_open u ∧ is_open v ∧ x ∈ u ∧ y ∈ v ∧ s ⊆ u ∪ v ∧ u ∩ v = ∅

theorem is_totally_separated_empty {α : Type u} [topological_space α] : is_totally_separated ∅ :=
  fun (x : α) => false.elim

theorem is_totally_separated_singleton {α : Type u} [topological_space α] {x : α} :
    is_totally_separated (singleton x) :=
  fun (p : α) (hp : p ∈ singleton x) (q : α) (hq : q ∈ singleton x) (hpq : p ≠ q) =>
    false.elim (hpq (Eq.symm (set.eq_of_mem_singleton hp) ▸ Eq.symm (set.eq_of_mem_singleton hq)))

theorem is_totally_disconnected_of_is_totally_separated {α : Type u} [topological_space α]
    {s : set α} (H : is_totally_separated s) : is_totally_disconnected s :=
  sorry

/-- A space is totally separated if any two points can be separated by two disjoint open sets
covering the whole space. -/
class totally_separated_space (α : Type u) [topological_space α] where
  is_totally_separated_univ : is_totally_separated set.univ

protected instance totally_separated_space.totally_disconnected_space (α : Type u)
    [topological_space α] [totally_separated_space α] : totally_disconnected_space α :=
  totally_disconnected_space.mk
    (is_totally_disconnected_of_is_totally_separated
      (totally_separated_space.is_totally_separated_univ α))

protected instance totally_separated_space.of_discrete (α : Type u_1) [topological_space α]
    [discrete_topology α] : totally_separated_space α :=
  sorry

end Mathlib


import topology.bounded_continuous_function

noncomputable theory
open set classical
local attribute [instance] prop_decidable

variables {X : Type*} [metric_space X] [compact_space X]

-- We adopt the notation of bounded countinuous function from mathlib
local infixr ` →ᵇ ` : 25 := bounded_continuous_function

/- This file contains all the definitions used throughout the project.

The main defintions are the following:
- lattice operation on bounded continuous functions defined as max and min
- standard defintion of the notion of uniform convergence of functions
- closure₀ as the closure of a set of bounded continuous functions
under lattice operations
- closure₂ as the closure of a set of bounded continuous functions
under lattice operations and uniform convergence to the limit
- boundary_points is the set of points in ℝ², z such that there exists a function
f in the indicated set of bounded continuous functions such that z = (f(x), f(y))
- converges_to_R2 is the standard definition of convergence in ℝ²
- closure' is the closure of a set of points in ℝ² under lattice operations
and convergence.
-/

/- max_bounded and min_bounded are trival -/
theorem max_bounded {f g : X → ℝ}
(hf :  ∃ (C : ℝ), ∀ (x y : X), dist (f x) (f y) ≤ C)
(hg :  ∃ (C : ℝ), ∀ (x y : X), dist (g x) (g y) ≤ C) :
∃ (C : ℝ), ∀ (x y : X), dist (max (f x) (g x)) (max (f y) (g y)) ≤ C := sorry

theorem min_bounded {f g : X → ℝ}
(hf :  ∃ (C : ℝ), ∀ (x y : X), dist (f x) (f y) ≤ C)
(hg :  ∃ (C : ℝ), ∀ (x y : X), dist (g x) (g y) ≤ C) :
∃ (C : ℝ), ∀ (x y : X), dist (min (f x) (g x)) (min (f y) (g y)) ≤ C := sorry

instance : has_sup (X →ᵇ ℝ) :=
⟨λ f g, ⟨λ x, max (f x) (g x), continuous.max f.2.1 g.2.1, max_bounded f.2.2 g.2.2⟩⟩

instance : has_inf (X →ᵇ ℝ) :=
⟨λ f g, ⟨λ x, min (f x) (g x), continuous.min f.2.1 g.2.1, min_bounded f.2.2 g.2.2⟩⟩

/- We define our own uniform convergence since mathlib's version is too powerful for
our purpose -/
def unif_converges_to {α} (F : ℕ → α → ℝ) (f : α → ℝ) :=
  ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, ∀ x : α, abs (f x - F n x) < ε

def closure₀ (M₀ : set (X →ᵇ ℝ)) :=
  {h : X →ᵇ ℝ | ∃ f g ∈ M₀, h = f ⊔ g ∨ h = f ⊓ g}

def closure₂ (M₀ : set (X →ᵇ ℝ)) :=
  {h : X →ᵇ ℝ | (∃ f g ∈ M₀, h = f ⊔ g ∨ h = f ⊓ g) ∨
  ∃ (F : ℕ → X →ᵇ ℝ) (H : ∀ i, F i ∈ closure₀ M₀), unif_converges_to (λ n, F n) h}

def boundary_points (M : set (X →ᵇ ℝ)) (x y : X) :=
  {z | ∃ (f : X →ᵇ ℝ) (hf : f ∈ M), (f x, f y) = z}

def converges_to_R2 (s : ℕ → ℝ × ℝ) (x) :=
  ∀ ε > 0, ∃ N : ℕ, ∀ n ≥ N, dist (s n) x < ε

-- We do not need to define inf and sup for tuples since its induced automatically!
-- try : #eval (1, 4) ⊔ (2, 3)

def closure' (S : set (ℝ × ℝ)) :=
  {z | (∃ r t ∈ S, r ⊔ t = z ∨ r ⊓ t = z) ∨
  ∃ (s : ℕ → ℝ × ℝ) (hs : ∀ n : ℕ, (s n) ∈ S), converges_to_R2 s z}

def has_seperate_points (M₀ : set (X →ᵇ ℝ)) :=
  ∀ x y : X, x ≠ y → ∃ (f : X →ᵇ ℝ) (hf : f ∈ M₀), f x ≠ f y

attribute [reducible] closure₀ closure₂ boundary_points closure'


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

import algebra.big_operators.fin
import algebra.geom_sum
import group_theory.perm.fin
import linear_algebra.matrix.determinant

/-!
# Vandermonde matrix

This file defines the `vandermonde` matrix and gives its determinant.

## Main definitions

 - `vandermonde v`: a square matrix with the `i, j`th entry equal to `v i ^ j`.

## Main results

 - `det_vandermonde`: `det (vandermonde v)` is the product of `v i - v j`, where
   `(i, j)` ranges over the unordered pairs.
-/

variables {R : Type*} [comm_ring R]

open equiv finset
open_locale big_operators matrix

namespace matrix

/-- `vandermonde v` is the square matrix with `i`th row equal to `1, v i, v i ^ 2, v i ^ 3, ...`.
-/
def vandermonde {n : ℕ} (v : fin n → R) : matrix (fin n) (fin n) R :=
λ i j, v i ^ (j : ℕ)

@[simp] lemma vandermonde_apply {n : ℕ} (v : fin n → R) (i j) :
  vandermonde v i j = v i ^ (j : ℕ) :=
rfl

@[simp] lemma vandermonde_cons {n : ℕ} (v0 : R) (v : fin n → R) :
  vandermonde (fin.cons v0 v : fin n.succ → R) =
    fin.cons (λ j, v0 ^ (j : ℕ)) (λ i, fin.cons 1 (λ j, v i * vandermonde v i j)) :=
begin
  ext i j,
  refine fin.cases (by simp) (λ i, _) i,
  refine fin.cases (by simp) (λ j, _) j,
  simp [pow_succ]
end

lemma vandermonde_succ {n : ℕ} (v : fin n.succ → R) :
  vandermonde v =
    fin.cons (λ j, v 0 ^ (j : ℕ))
      (λ i, fin.cons 1 (λ j, v i.succ * vandermonde (fin.tail v) i j)) :=
begin
  conv_lhs { rw [← fin.cons_self_tail v, vandermonde_cons] },
  simp only [fin.tail]
end

lemma vandermonde_mul_vandermonde_transpose {n : ℕ} (v w : fin n → R) (i j) :
  (vandermonde v ⬝ (vandermonde w)ᵀ) i j = ∑ (k : fin n), (v i * w j) ^ (k : ℕ) :=
by simp only [vandermonde_apply, matrix.mul_apply, matrix.transpose_apply, mul_pow]

lemma vandermonde_transpose_mul_vandermonde {n : ℕ} (v : fin n → R) (i j) :
  ((vandermonde v)ᵀ ⬝ vandermonde v) i j = ∑ (k : fin n), v k ^ (i + j : ℕ) :=
by simp only [vandermonde_apply, matrix.mul_apply, matrix.transpose_apply, pow_add]

lemma det_vandermonde {n : ℕ} (v : fin n → R) :
  det (vandermonde v) = ∏ i : fin n, ∏ j in Ioi i, (v j - v i) :=
begin
  unfold vandermonde,

  induction n with n ih,
  { exact det_eq_one_of_card_eq_zero (fintype.card_fin 0) },

  calc det (λ (i j : fin n.succ), v i ^ (j : ℕ))
      = det (λ (i j : fin n.succ), @fin.cons _ (λ _, R)
               (v 0 ^ (j : ℕ))
               (λ i, v (fin.succ i) ^ (j : ℕ) - v 0 ^ (j : ℕ)) i) :
    det_eq_of_forall_row_eq_smul_add_const (fin.cons 0 1) 0 (fin.cons_zero _ _) _
  ... = det (λ (i j : fin n), @fin.cons _ (λ _, R)
              (v 0 ^ (j.succ : ℕ))
              (λ (i : fin n), v (fin.succ i) ^ (j.succ : ℕ) - v 0 ^ (j.succ : ℕ))
              (fin.succ_above 0 i)) :
    by simp_rw [det_succ_column_zero, fin.sum_univ_succ, fin.cons_zero, minor, fin.cons_succ,
                fin.coe_zero, pow_zero, one_mul, sub_self, mul_zero, zero_mul,
                finset.sum_const_zero, add_zero]
  ... = det (λ (i j : fin n), (v (fin.succ i) - v 0) *
              (∑ k in finset.range (j + 1 : ℕ), v i.succ ^ k * v 0 ^ (j - k : ℕ))) :
    by { congr, ext i j, rw [fin.succ_above_zero, fin.cons_succ, fin.coe_succ, mul_comm],
         exact (geom_sum₂_mul (v i.succ) (v 0) (j + 1 : ℕ)).symm }
  ... = (∏ (i : fin n), (v (fin.succ i) - v 0)) * det (λ (i j : fin n),
    (∑ k in finset.range (j + 1 : ℕ), v i.succ ^ k * v 0 ^ (j - k : ℕ))) :
    det_mul_column (λ i, v (fin.succ i) - v 0) _
  ... = (∏ (i : fin n), (v (fin.succ i) - v 0)) * det (λ (i j : fin n), v (fin.succ i) ^ (j : ℕ)) :
    congr_arg ((*) _) _
  ... = ∏ i : fin n.succ, ∏ j in Ioi i, (v j - v i) :
    by simp_rw [ih (v ∘ fin.succ), fin.prod_univ_succ, fin.prod_Ioi_zero, fin.prod_Ioi_succ],
  { intros i j,
    rw fin.cons_zero,
    refine fin.cases _ (λ i, _) i,
    { simp },
    rw [fin.cons_succ, fin.cons_succ, pi.one_apply],
    ring },
  { cases n,
    { simp only [det_eq_one_of_card_eq_zero (fintype.card_fin 0)] },
    apply det_eq_of_forall_col_eq_smul_add_pred (λ i, v 0),
    { intro j,
      simp },
    { intros i j,
      simp only [smul_eq_mul, pi.add_apply, fin.coe_succ, fin.coe_cast_succ, pi.smul_apply],
      rw [finset.sum_range_succ, add_comm, tsub_self, pow_zero, mul_one, finset.mul_sum],
      congr' 1,
      refine finset.sum_congr rfl (λ i' hi', _),
      rw [mul_left_comm (v 0), nat.succ_sub, pow_succ],
      exact nat.lt_succ_iff.mp (finset.mem_range.mp hi') } }
end

lemma det_vandermonde_eq_zero_iff [is_domain R] {n : ℕ} {v : fin n → R} :
  det (vandermonde v) = 0 ↔ ∃ (i j : fin n), v i = v j ∧ i ≠ j :=
begin
  split,
  { simp only [det_vandermonde v, finset.prod_eq_zero_iff, sub_eq_zero, forall_exists_index],
    exact λ i _ j h₁ h₂, ⟨j, i, h₂, (mem_Ioi.mp h₁).ne'⟩ },
  { simp only [ne.def, forall_exists_index, and_imp],
    refine λ i j h₁ h₂, matrix.det_zero_of_row_eq h₂ (funext $ λ k, _),
    rw [vandermonde_apply, vandermonde_apply, h₁], }
end

lemma det_vandermonde_ne_zero_iff [is_domain R] {n : ℕ} {v : fin n → R} :
  det (vandermonde v) ≠ 0 ↔ function.injective v :=
by simpa only [det_vandermonde_eq_zero_iff, ne.def, not_exists, not_and, not_not]

end matrix


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Kevin Kappelmann

! This file was ported from Lean 3 source module data.rat.floor
! leanprover-community/mathlib commit be24ec5de6701447e5df5ca75400ffee19d65659
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.Floor
import Mathbin.Algebra.EuclideanDomain.Instances
import Mathbin.Tactic.FieldSimp

/-!
# Floor Function for Rational Numbers

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

## Summary

We define the `floor` function and the `floor_ring` instance on `ℚ`. Some technical lemmas relating
`floor` to integer division and modulo arithmetic are derived as well as some simple inequalities.

## Tags

rat, rationals, ℚ, floor
-/

open Int

namespace Rat

variable {α : Type _} [LinearOrderedField α] [FloorRing α]

#print Rat.floor /-
/-- `floor q` is the largest integer `z` such that `z ≤ q` -/
protected def floor : ℚ → ℤ
  | ⟨n, d, h, c⟩ => n / d
#align rat.floor Rat.floor
-/

/- warning: rat.le_floor -> Rat.le_floor is a dubious translation:
lean 3 declaration is
  forall {z : Int} {r : Rat}, Iff (LE.le.{0} Int Int.hasLe z (Rat.floor r)) (LE.le.{0} Rat Rat.hasLe ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) z) r)
but is expected to have type
  forall {z : Int} {r : Rat}, Iff (LE.le.{0} Int Int.instLEInt z (Rat.floor r)) (LE.le.{0} Rat Rat.instLERat (Int.cast.{0} Rat Rat.instIntCastRat z) r)
Case conversion may be inaccurate. Consider using '#align rat.le_floor Rat.le_floorₓ'. -/
protected theorem le_floor {z : ℤ} : ∀ {r : ℚ}, z ≤ Rat.floor r ↔ (z : ℚ) ≤ r
  | ⟨n, d, h, c⟩ => by
    simp [Rat.floor]
    rw [num_denom']
    have h' := Int.ofNat_lt.2 h
    conv =>
      rhs
      rw [coe_int_eq_mk, Rat.le_def zero_lt_one h', mul_one]
    exact Int.le_ediv_iff_mul_le h'
#align rat.le_floor Rat.le_floor

instance : FloorRing ℚ :=
  FloorRing.ofFloor ℚ Rat.floor fun a z => Rat.le_floor.symm

/- warning: rat.floor_def -> Rat.floor_def is a dubious translation:
lean 3 declaration is
  forall {q : Rat}, Eq.{1} Int (Int.floor.{0} Rat Rat.linearOrderedRing Rat.floorRing q) (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.hasDiv) (Rat.num q) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Rat.den q)))
but is expected to have type
  forall {q : Rat}, Eq.{1} Int (Int.floor.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat q) (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.instDivInt_1) (Rat.num q) (Nat.cast.{0} Int instNatCastInt (Rat.den q)))
Case conversion may be inaccurate. Consider using '#align rat.floor_def Rat.floor_defₓ'. -/
protected theorem floor_def {q : ℚ} : ⌊q⌋ = q.num / q.den :=
  by
  cases q
  rfl
#align rat.floor_def Rat.floor_def

/- warning: rat.floor_int_div_nat_eq_div -> Rat.floor_int_div_nat_eq_div is a dubious translation:
lean 3 declaration is
  forall {n : Int} {d : Nat}, Eq.{1} Int (Int.floor.{0} Rat Rat.linearOrderedRing Rat.floorRing (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.hasDiv) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) d))) (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.hasDiv) n ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) d))
but is expected to have type
  forall {n : Int} {d : Nat}, Eq.{1} Int (Int.floor.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.instDivRat) (Int.cast.{0} Rat Rat.instIntCastRat n) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.instLinearOrderedRingRat)))) d))) (HDiv.hDiv.{0, 0, 0} Int Int Int (instHDiv.{0} Int Int.instDivInt_1) n (Nat.cast.{0} Int instNatCastInt d))
Case conversion may be inaccurate. Consider using '#align rat.floor_int_div_nat_eq_div Rat.floor_int_div_nat_eq_divₓ'. -/
theorem floor_int_div_nat_eq_div {n : ℤ} {d : ℕ} : ⌊(↑n : ℚ) / (↑d : ℚ)⌋ = n / (↑d : ℤ) :=
  by
  rw [Rat.floor_def]
  obtain rfl | hd := @eq_zero_or_pos _ _ d
  · simp
  set q := (n : ℚ) / d with q_eq
  obtain ⟨c, n_eq_c_mul_num, d_eq_c_mul_denom⟩ : ∃ c, n = c * q.num ∧ (d : ℤ) = c * q.denom :=
    by
    rw [q_eq]
    exact_mod_cast @Rat.exists_eq_mul_div_num_and_eq_mul_div_den n d (by exact_mod_cast hd.ne')
  rw [n_eq_c_mul_num, d_eq_c_mul_denom]
  refine' (Int.mul_ediv_mul_of_pos _ _ <| pos_of_mul_pos_left _ <| Int.coe_nat_nonneg q.denom).symm
  rwa [← d_eq_c_mul_denom, Int.coe_nat_pos]
#align rat.floor_int_div_nat_eq_div Rat.floor_int_div_nat_eq_div

/- warning: rat.floor_cast -> Rat.floor_cast is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{1} Int (Int.floor.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Rat α (HasLiftT.mk.{1, succ u1} Rat α (CoeTCₓ.coe.{1, succ u1} Rat α (Rat.castCoe.{u1} α (DivisionRing.toHasRatCast.{u1} α (Field.toDivisionRing.{u1} α (LinearOrderedField.toField.{u1} α _inst_1)))))) x)) (Int.floor.{0} Rat Rat.linearOrderedRing Rat.floorRing x)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{1} Int (Int.floor.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 (Rat.cast.{u1} α (LinearOrderedField.toRatCast.{u1} α _inst_1) x)) (Int.floor.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat x)
Case conversion may be inaccurate. Consider using '#align rat.floor_cast Rat.floor_castₓ'. -/
@[simp, norm_cast]
theorem floor_cast (x : ℚ) : ⌊(x : α)⌋ = ⌊x⌋ :=
  floor_eq_iff.2 (by exact_mod_cast floor_eq_iff.1 (Eq.refl ⌊x⌋))
#align rat.floor_cast Rat.floor_cast

/- warning: rat.ceil_cast -> Rat.ceil_cast is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{1} Int (Int.ceil.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Rat α (HasLiftT.mk.{1, succ u1} Rat α (CoeTCₓ.coe.{1, succ u1} Rat α (Rat.castCoe.{u1} α (DivisionRing.toHasRatCast.{u1} α (Field.toDivisionRing.{u1} α (LinearOrderedField.toField.{u1} α _inst_1)))))) x)) (Int.ceil.{0} Rat Rat.linearOrderedRing Rat.floorRing x)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{1} Int (Int.ceil.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 (Rat.cast.{u1} α (LinearOrderedField.toRatCast.{u1} α _inst_1) x)) (Int.ceil.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat x)
Case conversion may be inaccurate. Consider using '#align rat.ceil_cast Rat.ceil_castₓ'. -/
@[simp, norm_cast]
theorem ceil_cast (x : ℚ) : ⌈(x : α)⌉ = ⌈x⌉ := by
  rw [← neg_inj, ← floor_neg, ← floor_neg, ← Rat.cast_neg, Rat.floor_cast]
#align rat.ceil_cast Rat.ceil_cast

/- warning: rat.round_cast -> Rat.round_cast is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{1} Int (round.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Rat α (HasLiftT.mk.{1, succ u1} Rat α (CoeTCₓ.coe.{1, succ u1} Rat α (Rat.castCoe.{u1} α (DivisionRing.toHasRatCast.{u1} α (Field.toDivisionRing.{u1} α (LinearOrderedField.toField.{u1} α _inst_1)))))) x)) (round.{0} Rat Rat.linearOrderedRing Rat.floorRing x)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{1} Int (round.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 (Rat.cast.{u1} α (LinearOrderedField.toRatCast.{u1} α _inst_1) x)) (round.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat x)
Case conversion may be inaccurate. Consider using '#align rat.round_cast Rat.round_castₓ'. -/
@[simp, norm_cast]
theorem round_cast (x : ℚ) : round (x : α) = round x :=
  by
  have : ((x + 1 / 2 : ℚ) : α) = x + 1 / 2 := by simp
  rw [round_eq, round_eq, ← this, floor_cast]
#align rat.round_cast Rat.round_cast

/- warning: rat.cast_fract -> Rat.cast_fract is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{succ u1} α ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Rat α (HasLiftT.mk.{1, succ u1} Rat α (CoeTCₓ.coe.{1, succ u1} Rat α (Rat.castCoe.{u1} α (DivisionRing.toHasRatCast.{u1} α (Field.toDivisionRing.{u1} α (LinearOrderedField.toField.{u1} α _inst_1)))))) (Int.fract.{0} Rat Rat.linearOrderedRing Rat.floorRing x)) (Int.fract.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Rat α (HasLiftT.mk.{1, succ u1} Rat α (CoeTCₓ.coe.{1, succ u1} Rat α (Rat.castCoe.{u1} α (DivisionRing.toHasRatCast.{u1} α (Field.toDivisionRing.{u1} α (LinearOrderedField.toField.{u1} α _inst_1)))))) x))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedField.{u1} α] [_inst_2 : FloorRing.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1))] (x : Rat), Eq.{succ u1} α (Rat.cast.{u1} α (LinearOrderedField.toRatCast.{u1} α _inst_1) (Int.fract.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat x)) (Int.fract.{u1} α (LinearOrderedCommRing.toLinearOrderedRing.{u1} α (LinearOrderedField.toLinearOrderedCommRing.{u1} α _inst_1)) _inst_2 (Rat.cast.{u1} α (LinearOrderedField.toRatCast.{u1} α _inst_1) x))
Case conversion may be inaccurate. Consider using '#align rat.cast_fract Rat.cast_fractₓ'. -/
@[simp, norm_cast]
theorem cast_fract (x : ℚ) : (↑(fract x) : α) = fract x := by
  simp only [fract, cast_sub, cast_coe_int, floor_cast]
#align rat.cast_fract Rat.cast_fract

end Rat

/- warning: int.mod_nat_eq_sub_mul_floor_rat_div -> Int.mod_nat_eq_sub_mul_floor_rat_div is a dubious translation:
lean 3 declaration is
  forall {n : Int} {d : Nat}, Eq.{1} Int (HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.hasMod) n ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) d)) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) n (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) d) (Int.floor.{0} Rat Rat.linearOrderedRing Rat.floorRing (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.hasDiv) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Int Rat (HasLiftT.mk.{1, 1} Int Rat (CoeTCₓ.coe.{1, 1} Int Rat (Int.castCoe.{0} Rat Rat.hasIntCast))) n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) d)))))
but is expected to have type
  forall {n : Int} {d : Nat}, Eq.{1} Int (HMod.hMod.{0, 0, 0} Int Int Int (instHMod.{0} Int Int.instModInt_1) n (Nat.cast.{0} Int instNatCastInt d)) (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) n (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) (Nat.cast.{0} Int instNatCastInt d) (Int.floor.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.instDivRat) (Int.cast.{0} Rat Rat.instIntCastRat n) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.instLinearOrderedRingRat)))) d)))))
Case conversion may be inaccurate. Consider using '#align int.mod_nat_eq_sub_mul_floor_rat_div Int.mod_nat_eq_sub_mul_floor_rat_divₓ'. -/
theorem Int.mod_nat_eq_sub_mul_floor_rat_div {n : ℤ} {d : ℕ} : n % d = n - d * ⌊(n : ℚ) / d⌋ := by
  rw [eq_sub_of_add_eq <| Int.emod_add_ediv n d, Rat.floor_int_div_nat_eq_div]
#align int.mod_nat_eq_sub_mul_floor_rat_div Int.mod_nat_eq_sub_mul_floor_rat_div

/- warning: nat.coprime_sub_mul_floor_rat_div_of_coprime -> Nat.coprime_sub_mul_floor_rat_div_of_coprime is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {d : Nat}, (Nat.coprime n d) -> (Nat.coprime (Int.natAbs (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) n) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) d) (Int.floor.{0} Rat Rat.linearOrderedRing Rat.floorRing (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.hasDiv) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Rat (HasLiftT.mk.{1, 1} Nat Rat (CoeTCₓ.coe.{1, 1} Nat Rat (Nat.castCoe.{0} Rat (AddMonoidWithOne.toNatCast.{0} Rat (AddGroupWithOne.toAddMonoidWithOne.{0} Rat (AddCommGroupWithOne.toAddGroupWithOne.{0} Rat (Ring.toAddCommGroupWithOne.{0} Rat (DivisionRing.toRing.{0} Rat Rat.divisionRing)))))))) d)))))) d)
but is expected to have type
  forall {n : Nat} {d : Nat}, (Nat.coprime n d) -> (Nat.coprime (Int.natAbs (HSub.hSub.{0, 0, 0} Int Int Int (instHSub.{0} Int Int.instSubInt) (Nat.cast.{0} Int instNatCastInt n) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) (Nat.cast.{0} Int instNatCastInt d) (Int.floor.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat (HDiv.hDiv.{0, 0, 0} Rat Rat Rat (instHDiv.{0} Rat Rat.instDivRat) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.instLinearOrderedRingRat)))) n) (Nat.cast.{0} Rat (NonAssocRing.toNatCast.{0} Rat (Ring.toNonAssocRing.{0} Rat (StrictOrderedRing.toRing.{0} Rat (LinearOrderedRing.toStrictOrderedRing.{0} Rat Rat.instLinearOrderedRingRat)))) d)))))) d)
Case conversion may be inaccurate. Consider using '#align nat.coprime_sub_mul_floor_rat_div_of_coprime Nat.coprime_sub_mul_floor_rat_div_of_coprimeₓ'. -/
theorem Nat.coprime_sub_mul_floor_rat_div_of_coprime {n d : ℕ} (n_coprime_d : n.coprime d) :
    ((n : ℤ) - d * ⌊(n : ℚ) / d⌋).natAbs.coprime d :=
  by
  have : (n : ℤ) % d = n - d * ⌊(n : ℚ) / d⌋ := Int.mod_nat_eq_sub_mul_floor_rat_div
  rw [← this]
  have : d.coprime n := n_coprime_d.symm
  rwa [Nat.coprime, Nat.gcd_rec] at this
#align nat.coprime_sub_mul_floor_rat_div_of_coprime Nat.coprime_sub_mul_floor_rat_div_of_coprime

namespace Rat

/- warning: rat.num_lt_succ_floor_mul_denom -> Rat.num_lt_succ_floor_mul_den is a dubious translation:
lean 3 declaration is
  forall (q : Rat), LT.lt.{0} Int Int.hasLt (Rat.num q) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.hasMul) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.hasAdd) (Int.floor.{0} Rat Rat.linearOrderedRing Rat.floorRing q) (OfNat.ofNat.{0} Int 1 (OfNat.mk.{0} Int 1 (One.one.{0} Int Int.hasOne)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Int (HasLiftT.mk.{1, 1} Nat Int (CoeTCₓ.coe.{1, 1} Nat Int (coeBase.{1, 1} Nat Int Int.hasCoe))) (Rat.den q)))
but is expected to have type
  forall (q : Rat), LT.lt.{0} Int Int.instLTInt (Rat.num q) (HMul.hMul.{0, 0, 0} Int Int Int (instHMul.{0} Int Int.instMulInt) (HAdd.hAdd.{0, 0, 0} Int Int Int (instHAdd.{0} Int Int.instAddInt) (Int.floor.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat q) (OfNat.ofNat.{0} Int 1 (instOfNatInt 1))) (Nat.cast.{0} Int instNatCastInt (Rat.den q)))
Case conversion may be inaccurate. Consider using '#align rat.num_lt_succ_floor_mul_denom Rat.num_lt_succ_floor_mul_denₓ'. -/
theorem num_lt_succ_floor_mul_den (q : ℚ) : q.num < (⌊q⌋ + 1) * q.den :=
  by
  suffices (q.num : ℚ) < (⌊q⌋ + 1) * q.denom by exact_mod_cast this
  suffices (q.num : ℚ) < (q - fract q + 1) * q.denom
    by
    have : (⌊q⌋ : ℚ) = q - fract q := eq_sub_of_add_eq <| floor_add_fract q
    rwa [this]
  suffices (q.num : ℚ) < q.num + (1 - fract q) * q.denom
    by
    have : (q - fract q + 1) * q.denom = q.num + (1 - fract q) * q.denom
    calc
      (q - fract q + 1) * q.denom = (q + (1 - fract q)) * q.denom := by ring
      _ = q * q.denom + (1 - fract q) * q.denom := by rw [add_mul]
      _ = q.num + (1 - fract q) * q.denom := by simp

    rwa [this]
  suffices 0 < (1 - fract q) * q.denom
    by
    rw [← sub_lt_iff_lt_add']
    simpa
  have : 0 < 1 - fract q := by
    have : fract q < 1 := fract_lt_one q
    have : 0 + fract q < 1 := by simp [this]
    rwa [lt_sub_iff_add_lt]
  exact mul_pos this (by exact_mod_cast q.pos)
#align rat.num_lt_succ_floor_mul_denom Rat.num_lt_succ_floor_mul_den

/- warning: rat.fract_inv_num_lt_num_of_pos -> Rat.fract_inv_num_lt_num_of_pos is a dubious translation:
lean 3 declaration is
  forall {q : Rat}, (LT.lt.{0} Rat Rat.hasLt (OfNat.ofNat.{0} Rat 0 (OfNat.mk.{0} Rat 0 (Zero.zero.{0} Rat Rat.hasZero))) q) -> (LT.lt.{0} Int Int.hasLt (Rat.num (Int.fract.{0} Rat Rat.linearOrderedRing Rat.floorRing (Inv.inv.{0} Rat Rat.hasInv q))) (Rat.num q))
but is expected to have type
  forall {q : Rat}, (LT.lt.{0} Rat Rat.instLTRat_1 (OfNat.ofNat.{0} Rat 0 (Rat.instOfNatRat 0)) q) -> (LT.lt.{0} Int Int.instLTInt (Rat.num (Int.fract.{0} Rat Rat.instLinearOrderedRingRat Rat.instFloorRingRatInstLinearOrderedRingRat (Inv.inv.{0} Rat Rat.instInvRat q))) (Rat.num q))
Case conversion may be inaccurate. Consider using '#align rat.fract_inv_num_lt_num_of_pos Rat.fract_inv_num_lt_num_of_posₓ'. -/
theorem fract_inv_num_lt_num_of_pos {q : ℚ} (q_pos : 0 < q) : (fract q⁻¹).num < q.num :=
  by
  -- we know that the numerator must be positive
  have q_num_pos : 0 < q.num := rat.num_pos_iff_pos.elim_right q_pos
  -- we will work with the absolute value of the numerator, which is equal to the numerator
  have q_num_abs_eq_q_num : (q.num.nat_abs : ℤ) = q.num := Int.natAbs_of_nonneg q_num_pos.le
  set q_inv := (q.denom : ℚ) / q.num with q_inv_def
  have q_inv_eq : q⁻¹ = q_inv := Rat.inv_def''
  suffices (q_inv - ⌊q_inv⌋).num < q.num by rwa [q_inv_eq]
  suffices ((q.denom - q.num * ⌊q_inv⌋ : ℚ) / q.num).num < q.num by
    field_simp [this, ne_of_gt q_num_pos]
  suffices (q.denom : ℤ) - q.num * ⌊q_inv⌋ < q.num
    by
    -- use that `q.num` and `q.denom` are coprime to show that the numerator stays unreduced
    have : ((q.denom - q.num * ⌊q_inv⌋ : ℚ) / q.num).num = q.denom - q.num * ⌊q_inv⌋ :=
      by
      suffices ((q.denom : ℤ) - q.num * ⌊q_inv⌋).natAbs.coprime q.num.nat_abs by
        exact_mod_cast Rat.num_div_eq_of_coprime q_num_pos this
      have tmp := Nat.coprime_sub_mul_floor_rat_div_of_coprime q.cop.symm
      simpa only [Nat.cast_natAbs, abs_of_nonneg q_num_pos.le] using tmp
    rwa [this]
  -- to show the claim, start with the following inequality
  have q_inv_num_denom_ineq : q⁻¹.num - ⌊q⁻¹⌋ * q⁻¹.den < q⁻¹.den :=
    by
    have : q⁻¹.num < (⌊q⁻¹⌋ + 1) * q⁻¹.den := Rat.num_lt_succ_floor_mul_den q⁻¹
    have : q⁻¹.num < ⌊q⁻¹⌋ * q⁻¹.den + q⁻¹.den := by rwa [right_distrib, one_mul] at this
    rwa [← sub_lt_iff_lt_add'] at this
  -- use that `q.num` and `q.denom` are coprime to show that q_inv is the unreduced reciprocal
  -- of `q`
  have : q_inv.num = q.denom ∧ q_inv.denom = q.num.nat_abs :=
    by
    have coprime_q_denom_q_num : q.denom.coprime q.num.nat_abs := q.cop.symm
    have : Int.natAbs q.denom = q.denom := by simp
    rw [← this] at coprime_q_denom_q_num
    rw [q_inv_def]
    constructor
    · exact_mod_cast Rat.num_div_eq_of_coprime q_num_pos coprime_q_denom_q_num
    · suffices (((q.denom : ℚ) / q.num).den : ℤ) = q.num.nat_abs by exact_mod_cast this
      rw [q_num_abs_eq_q_num]
      exact_mod_cast Rat.den_div_eq_of_coprime q_num_pos coprime_q_denom_q_num
  rwa [q_inv_eq, this.left, this.right, q_num_abs_eq_q_num, mul_comm] at q_inv_num_denom_ineq
#align rat.fract_inv_num_lt_num_of_pos Rat.fract_inv_num_lt_num_of_pos

end Rat


variables (α : Type) (p q : α → Prop)
variable r : Prop

example : α → ((∀ x : α, r) ↔ r) := begin
intro,
apply iff.intro, {
  intro f, exact f a
}, {
  intros, assumption
}
end

example : (∀ x, p x ∨ r) ↔ (∀ x, p x) ∨ r := begin
apply iff.intro, {
  intro h,
  apply classical.by_cases, {
    assume hr: r, right, assumption
  }, {
    assume hnr: ¬ r, left,
    intro,
    apply or.elim (h x), {
      intro, assumption,
    }, {
      intro, contradiction,
    }
  }
}, {
  intro h,
  intro,
  apply or.elim h, {
    intro h1, left, exact h1 x
  }, {
    intro, right, assumption
  }
}
end

example : (∀ x, r → p x) ↔ (r → ∀ x, p x) := begin
apply iff.intro, {
  intros h hr x, exact h x hr
}, {
  intros h x hr, exact h hr x
}
end


-- Author: Chris Hughes
import data.nat.modeq data.set.finite
open nat
local attribute [instance, priority 0] classical.prop_decidable
namespace set
open function
universe u
variable α : Type u

-- Two lemmas courtesy of Johannes Hölzl via gitter, due to be added to mathlib
lemma infinite_univ_nat : infinite (univ : set ℕ) :=
 assume (h : finite (univ : set ℕ)),
 let ⟨n, hn⟩ := finset.exists_nat_subset_range h.to_finset in
 have n ∈ finset.range n, from finset.subset_iff.mpr hn $ by simp,
 by simp * at *

lemma not_injective_nat_fintype [fintype α] [decidable_eq α] {f : ℕ → α} : ¬ injective f :=
 assume (h : injective f),
 have finite (f '' univ),
   from finite_subset (finset.finite_to_set $ fintype.elems α) (assume a h, fintype.complete a),
 have finite (univ : set ℕ), from finite_of_finite_image h this,
 infinite_univ_nat this

end set

noncomputable instance subtype.fintype_le_nat (n : ℕ) : fintype {i : ℕ // i ≤ n} :=
  classical.choice $ set.finite_le_nat n

theorem sheet_7_2e (f : ℕ → ℕ) {d : ℕ} : d > 0 → ∃ a b, a ≠ b ∧ f a ≡ f b [MOD d]:=begin
  assume hd,
  unfold modeq,
  apply by_contradiction,
  assume h,
  let g : ℕ → {i // i ≤ d} := λ n, ⟨f n % d, le_of_lt (mod_lt (f n) hd)⟩,
  have h_inj : function.injective g,
    assume a b,
    simp [g],
    rw not_exists at h, have := h a, rw not_exists at this, have := this b,
    rwa [not_and',not_not] at this,
  exact set.not_injective_nat_fintype {i // i ≤ d} h_inj,
end
#print sheet_7_2e