/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.local_homeomorph
import Mathlib.PostPort

universes u l u_5 u_1 u_6 u_2 u_3 u_4

namespace Mathlib

/-!
# Charted spaces

A smooth manifold is a topological space `M` locally modelled on a euclidean space (or a euclidean
half-space for manifolds with boundaries, or an infinite dimensional vector space for more general
notions of manifolds), i.e., the manifold is covered by open subsets on which there are local
homeomorphisms (the charts) going to a model space `H`, and the changes of charts should be smooth
maps.

In this file, we introduce a general framework describing these notions, where the model space is an
arbitrary topological space. We avoid the word *manifold*, which should be reserved for the
situation where the model space is a (subset of a) vector space, and use the terminology
*charted space* instead.

If the changes of charts satisfy some additional property (for instance if they are smooth), then
`M` inherits additional structure (it makes sense to talk about smooth manifolds). There are
therefore two different ingredients in a charted space:
* the set of charts, which is data
* the fact that changes of charts belong to some group (in fact groupoid), which is additional Prop.

We separate these two parts in the definition: the charted space structure is just the set of
charts, and then the different smoothness requirements (smooth manifold, orientable manifold,
contact manifold, and so on) are additional properties of these charts. These properties are
formalized through the notion of structure groupoid, i.e., a set of local homeomorphisms stable
under composition and inverse, to which the change of coordinates should belong.

## Main definitions

* `structure_groupoid H` : a subset of local homeomorphisms of `H` stable under composition,
  inverse and restriction (ex: local diffeos).
* `continuous_groupoid H` : the groupoid of all local homeomorphisms of `H`
* `charted_space H M` : charted space structure on `M` modelled on `H`, given by an atlas of
  local homeomorphisms from `M` to `H` whose sources cover `M`. This is a type class.
* `has_groupoid M G` : when `G` is a structure groupoid on `H` and `M` is a charted space
  modelled on `H`, require that all coordinate changes belong to `G`. This is a type class.
* `atlas H M` : when `M` is a charted space modelled on `H`, the atlas of this charted
  space structure, i.e., the set of charts.
* `G.maximal_atlas M` : when `M` is a charted space modelled on `H` and admitting `G` as a
  structure groupoid, one can consider all the local homeomorphisms from `M` to `H` such that
  changing coordinate from any chart to them belongs to `G`. This is a larger atlas, called the
  maximal atlas (for the groupoid `G`).
* `structomorph G M M'` : the type of diffeomorphisms between the charted spaces `M` and `M'` for
  the groupoid `G`. We avoid the word diffeomorphism, keeping it for the smooth category.

As a basic example, we give the instance
`instance charted_space_model_space (H : Type*) [topological_space H] : charted_space H H`
saying that a topological space is a charted space over itself, with the identity as unique chart.
This charted space structure is compatible with any groupoid.

Additional useful definitions:

* `pregroupoid H` : a subset of local mas of `H` stable under composition and
  restriction, but not inverse (ex: smooth maps)
* `groupoid_of_pregroupoid` : construct a groupoid from a pregroupoid, by requiring that a map and
  its inverse both belong to the pregroupoid (ex: construct diffeos from smooth maps)
* `chart_at H x` is a preferred chart at `x : M` when `M` has a charted space structure modelled on
  `H`.
* `G.compatible he he'` states that, for any two charts `e` and `e'` in the atlas, the composition
  of `e.symm` and `e'` belongs to the groupoid `G` when `M` admits `G` as a structure groupoid.
* `G.compatible_of_mem_maximal_atlas he he'` states that, for any two charts `e` and `e'` in the
  maximal atlas associated to the groupoid `G`, the composition of `e.symm` and `e'` belongs to the
  `G` if `M` admits `G` as a structure groupoid.
* `charted_space_core.to_charted_space`: consider a space without a topology, but endowed with a set
  of charts (which are local equivs) for which the change of coordinates are local homeos. Then
  one can construct a topology on the space for which the charts become local homeos, defining
  a genuine charted space structure.

## Implementation notes

The atlas in a charted space is *not* a maximal atlas in general: the notion of maximality depends
on the groupoid one considers, and changing groupoids changes the maximal atlas. With the current
formalization, it makes sense first to choose the atlas, and then to ask whether this precise atlas
defines a smooth manifold, an orientable manifold, and so on. A consequence is that structomorphisms
between `M` and `M'` do *not* induce a bijection between the atlases of `M` and `M'`: the
definition is only that, read in charts, the structomorphism locally belongs to the groupoid under
consideration. (This is equivalent to inducing a bijection between elements of the maximal atlas).
A consequence is that the invariance under structomorphisms of properties defined in terms of the
atlas is not obvious in general, and could require some work in theory (amounting to the fact
that these properties only depend on the maximal atlas, for instance). In practice, this does not
create any real difficulty.

We use the letter `H` for the model space thinking of the case of manifolds with boundary, where the
model space is a half space.

Manifolds are sometimes defined as topological spaces with an atlas of local diffeomorphisms, and
sometimes as spaces with an atlas from which a topology is deduced. We use the former approach:
otherwise, there would be an instance from manifolds to topological spaces, which means that any
instance search for topological spaces would try to find manifold structures involving a yet
unknown model space, leading to problems. However, we also introduce the latter approach,
through a structure `charted_space_core` making it possible to construct a topology out of a set of
local equivs with compatibility conditions (but we do not register it as an instance).

In the definition of a charted space, the model space is written as an explicit parameter as there
can be several model spaces for a given topological space. For instance, a complex manifold
(modelled over `ℂ^n`) will also be seen sometimes as a real manifold modelled over `ℝ^(2n)`.

## Notations

In the locale `manifold`, we denote the composition of local homeomorphisms with `≫ₕ`, and the
composition of local equivs with `≫`.
-/

/- Notational shortcut for the composition of local homeomorphisms and local equivs, i.e.,
`local_homeomorph.trans` and `local_equiv.trans`.
Note that, as is usual for equivs, the composition is from left to right, hence the direction of
the arrow. -/

/- `simp` looks for subsingleton instances at every call. This turns out to be very
inefficient, especially in `simp`-heavy parts of the library such as the manifold code.
Disable two such instances to speed up things.
NB: this is just a hack. TODO: fix `simp` properly. -/

/-! ### Structure groupoids-/

/-! One could add to the definition of a structure groupoid the fact that the restriction of an
element of the groupoid to any open set still belongs to the groupoid.
(This is in Kobayashi-Nomizu.)
I am not sure I want this, for instance on `H × E` where `E` is a vector space, and the groupoid is
made of functions respecting the fibers and linear in the fibers (so that a charted space over this
groupoid is naturally a vector bundle) I prefer that the members of the groupoid are always
defined on sets of the form `s × E`.  There is a typeclass `closed_under_restriction` for groupoids
which have the restriction property.

The only nontrivial requirement is locality: if a local homeomorphism belongs to the groupoid
around each point in its domain of definition, then it belongs to the groupoid. Without this
requirement, the composition of structomorphisms does not have to be a structomorphism. Note that
this implies that a local homeomorphism with empty source belongs to any structure groupoid, as
it trivially satisfies this condition.

There is also a technical point, related to the fact that a local homeomorphism is by definition a
global map which is a homeomorphism when restricted to its source subset (and its values outside
of the source are not relevant). Therefore, we also require that being a member of the groupoid only
depends on the values on the source.

We use primes in the structure names as we will reformulate them below (without primes) using a
`has_mem` instance, writing `e ∈ G` instead of `e ∈ G.members`.
-/

/-- A structure groupoid is a set of local homeomorphisms of a topological space stable under
composition and inverse. They appear in the definition of the smoothness class of a manifold. -/
structure structure_groupoid (H : Type u) [topological_space H]
where
  members : set (local_homeomorph H H)
  trans' : ∀ (e e' : local_homeomorph H H), e ∈ members → e' ∈ members → local_homeomorph.trans e e' ∈ members
  symm' : ∀ (e : local_homeomorph H H), e ∈ members → local_homeomorph.symm e ∈ members
  id_mem' : local_homeomorph.refl H ∈ members
  locality' : ∀ (e : local_homeomorph H H),
  (∀ (x : H),
      x ∈ local_equiv.source (local_homeomorph.to_local_equiv e) →
        ∃ (s : set H), is_open s ∧ x ∈ s ∧ local_homeomorph.restr e s ∈ members) →
    e ∈ members
  eq_on_source' : ∀ (e e' : local_homeomorph H H), e ∈ members → e' ≈ e → e' ∈ members

protected instance structure_groupoid.has_mem {H : Type u} [topological_space H] : has_mem (local_homeomorph H H) (structure_groupoid H) :=
  has_mem.mk fun (e : local_homeomorph H H) (G : structure_groupoid H) => e ∈ structure_groupoid.members G

theorem structure_groupoid.trans {H : Type u} [topological_space H] (G : structure_groupoid H) {e : local_homeomorph H H} {e' : local_homeomorph H H} (he : e ∈ G) (he' : e' ∈ G) : local_homeomorph.trans e e' ∈ G :=
  structure_groupoid.trans' G e e' he he'

theorem structure_groupoid.symm {H : Type u} [topological_space H] (G : structure_groupoid H) {e : local_homeomorph H H} (he : e ∈ G) : local_homeomorph.symm e ∈ G :=
  structure_groupoid.symm' G e he

theorem structure_groupoid.id_mem {H : Type u} [topological_space H] (G : structure_groupoid H) : local_homeomorph.refl H ∈ G :=
  structure_groupoid.id_mem' G

theorem structure_groupoid.locality {H : Type u} [topological_space H] (G : structure_groupoid H) {e : local_homeomorph H H} (h : ∀ (x : H),
  x ∈ local_equiv.source (local_homeomorph.to_local_equiv e) →
    ∃ (s : set H), is_open s ∧ x ∈ s ∧ local_homeomorph.restr e s ∈ G) : e ∈ G :=
  structure_groupoid.locality' G e h

theorem structure_groupoid.eq_on_source {H : Type u} [topological_space H] (G : structure_groupoid H) {e : local_homeomorph H H} {e' : local_homeomorph H H} (he : e ∈ G) (h : e' ≈ e) : e' ∈ G :=
  structure_groupoid.eq_on_source' G e e' he h

/-- Partial order on the set of groupoids, given by inclusion of the members of the groupoid -/
protected instance structure_groupoid.partial_order {H : Type u} [topological_space H] : partial_order (structure_groupoid H) :=
  partial_order.lift structure_groupoid.members sorry

theorem structure_groupoid.le_iff {H : Type u} [topological_space H] {G₁ : structure_groupoid H} {G₂ : structure_groupoid H} : G₁ ≤ G₂ ↔ ∀ (e : local_homeomorph H H), e ∈ G₁ → e ∈ G₂ :=
  iff.rfl

/-- The trivial groupoid, containing only the identity (and maps with empty source, as this is
necessary from the definition) -/
def id_groupoid (H : Type u) [topological_space H] : structure_groupoid H :=
  structure_groupoid.mk
    (singleton (local_homeomorph.refl H) ∪
      set_of fun (e : local_homeomorph H H) => local_equiv.source (local_homeomorph.to_local_equiv e) = ∅)
    sorry sorry sorry sorry sorry

/-- Every structure groupoid contains the identity groupoid -/
protected instance structure_groupoid.order_bot {H : Type u} [topological_space H] : order_bot (structure_groupoid H) :=
  order_bot.mk (id_groupoid H) partial_order.le partial_order.lt sorry sorry sorry sorry

protected instance structure_groupoid.inhabited (H : Type u) [topological_space H] : Inhabited (structure_groupoid H) :=
  { default := id_groupoid H }

/-- To construct a groupoid, one may consider classes of local homeos such that both the function
and its inverse have some property. If this property is stable under composition,
one gets a groupoid. `pregroupoid` bundles the properties needed for this construction, with the
groupoid of smooth functions with smooth inverses as an application. -/
structure pregroupoid (H : Type u_5) [topological_space H]
where
  property : (H → H) → set H → Prop
  comp : ∀ {f g : H → H} {u v : set H},
  property f u → property g v → is_open u → is_open v → is_open (u ∩ f ⁻¹' v) → property (g ∘ f) (u ∩ f ⁻¹' v)
  id_mem : property id set.univ
  locality : ∀ {f : H → H} {u : set H},
  is_open u → (∀ (x : H), x ∈ u → ∃ (v : set H), is_open v ∧ x ∈ v ∧ property f (u ∩ v)) → property f u
  congr : ∀ {f g : H → H} {u : set H}, is_open u → (∀ (x : H), x ∈ u → g x = f x) → property f u → property g u

/-- Construct a groupoid of local homeos for which the map and its inverse have some property,
from a pregroupoid asserting that this property is stable under composition. -/
def pregroupoid.groupoid {H : Type u} [topological_space H] (PG : pregroupoid H) : structure_groupoid H :=
  structure_groupoid.mk
    (set_of
      fun (e : local_homeomorph H H) =>
        pregroupoid.property PG (⇑e) (local_equiv.source (local_homeomorph.to_local_equiv e)) ∧
          pregroupoid.property PG (⇑(local_homeomorph.symm e)) (local_equiv.target (local_homeomorph.to_local_equiv e)))
    sorry sorry sorry sorry sorry

theorem mem_groupoid_of_pregroupoid {H : Type u} [topological_space H] {PG : pregroupoid H} {e : local_homeomorph H H} : e ∈ pregroupoid.groupoid PG ↔
  pregroupoid.property PG (⇑e) (local_equiv.source (local_homeomorph.to_local_equiv e)) ∧
    pregroupoid.property PG (⇑(local_homeomorph.symm e)) (local_equiv.target (local_homeomorph.to_local_equiv e)) :=
  iff.rfl

theorem groupoid_of_pregroupoid_le {H : Type u} [topological_space H] (PG₁ : pregroupoid H) (PG₂ : pregroupoid H) (h : ∀ (f : H → H) (s : set H), pregroupoid.property PG₁ f s → pregroupoid.property PG₂ f s) : pregroupoid.groupoid PG₁ ≤ pregroupoid.groupoid PG₂ := sorry

theorem mem_pregroupoid_of_eq_on_source {H : Type u} [topological_space H] (PG : pregroupoid H) {e : local_homeomorph H H} {e' : local_homeomorph H H} (he' : e ≈ e') (he : pregroupoid.property PG (⇑e) (local_equiv.source (local_homeomorph.to_local_equiv e))) : pregroupoid.property PG (⇑e') (local_equiv.source (local_homeomorph.to_local_equiv e')) := sorry

/-- The pregroupoid of all local maps on a topological space `H` -/
def continuous_pregroupoid (H : Type u_1) [topological_space H] : pregroupoid H :=
  pregroupoid.mk (fun (f : H → H) (s : set H) => True) sorry trivial sorry sorry

protected instance pregroupoid.inhabited (H : Type u_1) [topological_space H] : Inhabited (pregroupoid H) :=
  { default := continuous_pregroupoid H }

/-- The groupoid of all local homeomorphisms on a topological space `H` -/
def continuous_groupoid (H : Type u_1) [topological_space H] : structure_groupoid H :=
  pregroupoid.groupoid (continuous_pregroupoid H)

/-- Every structure groupoid is contained in the groupoid of all local homeomorphisms -/
protected instance structure_groupoid.order_top {H : Type u} [topological_space H] : order_top (structure_groupoid H) :=
  order_top.mk (continuous_groupoid H) partial_order.le partial_order.lt sorry sorry sorry sorry

/-- A groupoid is closed under restriction if it contains all restrictions of its element local
homeomorphisms to open subsets of the source. -/
class closed_under_restriction {H : Type u} [topological_space H] (G : structure_groupoid H)
where
  closed_under_restriction : ∀ {e : local_homeomorph H H}, e ∈ G → ∀ (s : set H), is_open s → local_homeomorph.restr e s ∈ G

theorem closed_under_restriction' {H : Type u} [topological_space H] {G : structure_groupoid H} [closed_under_restriction G] {e : local_homeomorph H H} (he : e ∈ G) {s : set H} (hs : is_open s) : local_homeomorph.restr e s ∈ G :=
  closed_under_restriction.closed_under_restriction he s hs

/-- The trivial restriction-closed groupoid, containing only local homeomorphisms equivalent to the
restriction of the identity to the various open subsets. -/
def id_restr_groupoid {H : Type u} [topological_space H] : structure_groupoid H :=
  structure_groupoid.mk
    (set_of
      fun (e : local_homeomorph H H) => Exists fun {s : set H} => ∃ (h : is_open s), e ≈ local_homeomorph.of_set s h)
    sorry sorry sorry sorry sorry

theorem id_restr_groupoid_mem {H : Type u} [topological_space H] {s : set H} (hs : is_open s) : local_homeomorph.of_set s hs ∈ id_restr_groupoid :=
  Exists.intro s (Exists.intro hs (setoid.refl (local_homeomorph.of_set s hs)))

/-- The trivial restriction-closed groupoid is indeed `closed_under_restriction`. -/
protected instance closed_under_restriction_id_restr_groupoid {H : Type u} [topological_space H] : closed_under_restriction id_restr_groupoid := sorry

/-- A groupoid is closed under restriction if and only if it contains the trivial restriction-closed
groupoid. -/
theorem closed_under_restriction_iff_id_le {H : Type u} [topological_space H] (G : structure_groupoid H) : closed_under_restriction G ↔ id_restr_groupoid ≤ G := sorry

/-- The groupoid of all local homeomorphisms on a topological space `H` is closed under restriction.
-/
protected instance continuous_groupoid.closed_under_restriction {H : Type u} [topological_space H] : closed_under_restriction (continuous_groupoid H) :=
  iff.mpr (closed_under_restriction_iff_id_le (continuous_groupoid H))
    (eq.mpr
      ((fun (ᾰ ᾰ_1 : structure_groupoid H) (e_2 : ᾰ = ᾰ_1) (ᾰ_2 ᾰ_3 : structure_groupoid H) (e_3 : ᾰ_2 = ᾰ_3) =>
          congr (congr_arg LessEq e_2) e_3)
        id_restr_groupoid id_restr_groupoid (Eq.refl id_restr_groupoid) (continuous_groupoid H) ⊤
        (Eq.refl (continuous_groupoid H)))
      le_top)

/-! ### Charted spaces -/

/-- A charted space is a topological space endowed with an atlas, i.e., a set of local
homeomorphisms taking value in a model space `H`, called charts, such that the domains of the charts
cover the whole space. We express the covering property by chosing for each `x` a member
`chart_at H x` of the atlas containing `x` in its source: in the smooth case, this is convenient to
construct the tangent bundle in an efficient way.
The model space is written as an explicit parameter as there can be several model spaces for a
given topological space. For instance, a complex manifold (modelled over `ℂ^n`) will also be seen
sometimes as a real manifold over `ℝ^(2n)`.
-/
class charted_space (H : Type u_5) [topological_space H] (M : Type u_6) [topological_space M]
where
  atlas : set (local_homeomorph M H)
  chart_at : M → local_homeomorph M H
  mem_chart_source : ∀ (x : M), x ∈ local_equiv.source (local_homeomorph.to_local_equiv (chart_at x))
  chart_mem_atlas : ∀ (x : M), chart_at x ∈ atlas

/-- Any space is a charted_space modelled over itself, by just using the identity chart -/
protected instance charted_space_self (H : Type u_1) [topological_space H] : charted_space H H :=
  charted_space.mk (singleton (local_homeomorph.refl H)) (fun (x : H) => local_homeomorph.refl H) sorry sorry

/-- In the trivial charted_space structure of a space modelled over itself through the identity, the
atlas members are just the identity -/
@[simp] theorem charted_space_self_atlas {H : Type u_1} [topological_space H] {e : local_homeomorph H H} : e ∈ charted_space.atlas H H ↔ e = local_homeomorph.refl H := sorry

/-- In the model space, chart_at is always the identity -/
@[simp] theorem chart_at_self_eq {H : Type u_1} [topological_space H] {x : H} : charted_space.chart_at H x = local_homeomorph.refl H :=
  eq.mpr (id (Eq.refl (charted_space.chart_at H x = local_homeomorph.refl H)))
    (eq.mp (propext charted_space_self_atlas) (charted_space.chart_mem_atlas H x))

/-- Same thing as `H × H'`. We introduce it for technical reasons: a charted space `M` with model `H`
is a set of local charts from `M` to `H` covering the space. Every space is registered as a charted
space over itself, using the only chart `id`, in `manifold_model_space`. You can also define a product
of charted space `M` and `M'` (with model space `H × H'`) by taking the products of the charts. Now,
on `H × H'`, there are two charted space structures with model space `H × H'` itself, the one coming
from `manifold_model_space`, and the one coming from the product of the two `manifold_model_space` on
each component. They are equal, but not defeq (because the product of `id` and `id` is not defeq to
`id`), which is bad as we know. This expedient of renaming `H × H'` solves this problem. -/
def model_prod (H : Type u_1) (H' : Type u_2) :=
  H × H'

protected instance model_prod_inhabited {α : Type u_1} {β : Type u_2} [Inhabited α] [Inhabited β] : Inhabited (model_prod α β) :=
  { default := (Inhabited.default, Inhabited.default) }

protected instance model_prod.topological_space (H : Type u_1) [topological_space H] (H' : Type u_2) [topological_space H'] : topological_space (model_prod H H') :=
  prod.topological_space

/- Next lemma shows up often when dealing with derivatives, register it as simp. -/

@[simp] theorem model_prod_range_prod_id {H : Type u_1} {H' : Type u_2} {α : Type u_3} (f : H → α) : (set.range fun (p : model_prod H H') => (f (prod.fst p), prod.snd p)) = set.prod (set.range f) set.univ := sorry

/-- The product of two charted spaces is naturally a charted space, with the canonical
construction of the atlas of product maps. -/
protected instance prod_charted_space (H : Type u_1) [topological_space H] (M : Type u_2) [topological_space M] [charted_space H M] (H' : Type u_3) [topological_space H'] (M' : Type u_4) [topological_space M'] [charted_space H' M'] : charted_space (model_prod H H') (M × M') :=
  charted_space.mk
    (set_of
      fun (f : local_homeomorph (M × M') (model_prod H H')) =>
        ∃ (g : local_homeomorph M H),
          ∃ (H_1 : g ∈ charted_space.atlas H M),
            ∃ (h : local_homeomorph M' H'), ∃ (H_2 : h ∈ charted_space.atlas H' M'), f = local_homeomorph.prod g h)
    (fun (x : M × M') =>
      local_homeomorph.prod (charted_space.chart_at H (prod.fst x)) (charted_space.chart_at H' (prod.snd x)))
    sorry sorry

@[simp] theorem prod_charted_space_chart_at {H : Type u} {H' : Type u_1} {M : Type u_2} {M' : Type u_3} [topological_space H] [topological_space M] [charted_space H M] [topological_space H'] [topological_space M'] [charted_space H' M'] {x : M × M'} : charted_space.chart_at (model_prod H H') x =
  local_homeomorph.prod (charted_space.chart_at H (prod.fst x)) (charted_space.chart_at H' (prod.snd x)) :=
  rfl

/-! ### Constructing a topology from an atlas -/

/-- Sometimes, one may want to construct a charted space structure on a space which does not yet
have a topological structure, where the topology would come from the charts. For this, one needs
charts that are only local equivs, and continuity properties for their composition.
This is formalised in `charted_space_core`. -/
structure charted_space_core (H : Type u_5) [topological_space H] (M : Type u_6)
where
  atlas : set (local_equiv M H)
  chart_at : M → local_equiv M H
  mem_chart_source : ∀ (x : M), x ∈ local_equiv.source (chart_at x)
  chart_mem_atlas : ∀ (x : M), chart_at x ∈ atlas
  open_source : ∀ (e e' : local_equiv M H),
  e ∈ atlas → e' ∈ atlas → is_open (local_equiv.source (local_equiv.trans (local_equiv.symm e) e'))
  continuous_to_fun : ∀ (e e' : local_equiv M H),
  e ∈ atlas →
    e' ∈ atlas →
      continuous_on (⇑(local_equiv.trans (local_equiv.symm e) e'))
        (local_equiv.source (local_equiv.trans (local_equiv.symm e) e'))

namespace charted_space_core

/-- Topology generated by a set of charts on a Type. -/
protected def to_topological_space {H : Type u} {M : Type u_2} [topological_space H] (c : charted_space_core H M) : topological_space M :=
  topological_space.generate_from
    (set.Union
      fun (e : local_equiv M H) =>
        set.Union
          fun (he : e ∈ atlas c) =>
            set.Union
              fun (s : set H) => set.Union fun (s_open : is_open s) => singleton (⇑e ⁻¹' s ∩ local_equiv.source e))

theorem open_source' {H : Type u} {M : Type u_2} [topological_space H] (c : charted_space_core H M) {e : local_equiv M H} (he : e ∈ atlas c) : is_open (local_equiv.source e) := sorry

theorem open_target {H : Type u} {M : Type u_2} [topological_space H] (c : charted_space_core H M) {e : local_equiv M H} (he : e ∈ atlas c) : is_open (local_equiv.target e) := sorry

/-- An element of the atlas in a charted space without topology becomes a local homeomorphism
for the topology constructed from this atlas. The `local_homeomorph` version is given in this
definition. -/
def local_homeomorph {H : Type u} {M : Type u_2} [topological_space H] (c : charted_space_core H M) (e : local_equiv M H) (he : e ∈ atlas c) : local_homeomorph M H :=
  local_homeomorph.mk
    (local_equiv.mk (local_equiv.to_fun e) (local_equiv.inv_fun e) (local_equiv.source e) (local_equiv.target e)
      (local_equiv.map_source' e) (local_equiv.map_target' e) (local_equiv.left_inv' e) (local_equiv.right_inv' e))
    sorry sorry sorry sorry

/-- Given a charted space without topology, endow it with a genuine charted space structure with
respect to the topology constructed from the atlas. -/
def to_charted_space {H : Type u} {M : Type u_2} [topological_space H] (c : charted_space_core H M) : charted_space H M :=
  charted_space.mk
    (set.Union fun (e : local_equiv M H) => set.Union fun (he : e ∈ atlas c) => singleton (local_homeomorph c e he))
    (fun (x : M) => local_homeomorph c (chart_at c x) (chart_mem_atlas c x)) sorry sorry

end charted_space_core

/-! ### Charted space with a given structure groupoid -/

/-- A charted space has an atlas in a groupoid `G` if the change of coordinates belong to the
groupoid -/
class has_groupoid {H : Type u_5} [topological_space H] (M : Type u_6) [topological_space M] [charted_space H M] (G : structure_groupoid H)
where
  compatible : ∀ {e e' : local_homeomorph M H},
  e ∈ charted_space.atlas H M → e' ∈ charted_space.atlas H M → local_homeomorph.trans (local_homeomorph.symm e) e' ∈ G

/-- Reformulate in the `structure_groupoid` namespace the compatibility condition of charts in a
charted space admitting a structure groupoid, to make it more easily accessible with dot
notation. -/
theorem structure_groupoid.compatible {H : Type u_1} [topological_space H] (G : structure_groupoid H) {M : Type u_2} [topological_space M] [charted_space H M] [has_groupoid M G] {e : local_homeomorph M H} {e' : local_homeomorph M H} (he : e ∈ charted_space.atlas H M) (he' : e' ∈ charted_space.atlas H M) : local_homeomorph.trans (local_homeomorph.symm e) e' ∈ G :=
  has_groupoid.compatible G he he'

theorem has_groupoid_of_le {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] {G₁ : structure_groupoid H} {G₂ : structure_groupoid H} (h : has_groupoid M G₁) (hle : G₁ ≤ G₂) : has_groupoid M G₂ :=
  has_groupoid.mk
    fun (e e' : local_homeomorph M H) (he : e ∈ charted_space.atlas H M) (he' : e' ∈ charted_space.atlas H M) =>
      hle (has_groupoid.compatible G₁ he he')

theorem has_groupoid_of_pregroupoid {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] (PG : pregroupoid H) (h : ∀ {e e' : local_homeomorph M H},
  e ∈ charted_space.atlas H M →
    e' ∈ charted_space.atlas H M →
      pregroupoid.property PG (⇑(local_homeomorph.trans (local_homeomorph.symm e) e'))
        (local_equiv.source (local_homeomorph.to_local_equiv (local_homeomorph.trans (local_homeomorph.symm e) e')))) : has_groupoid M (pregroupoid.groupoid PG) :=
  has_groupoid.mk
    fun (e e' : local_homeomorph M H) (he : e ∈ charted_space.atlas H M) (he' : e' ∈ charted_space.atlas H M) =>
      iff.mpr mem_groupoid_of_pregroupoid { left := h he he', right := h he' he }

/-- The trivial charted space structure on the model space is compatible with any groupoid -/
protected instance has_groupoid_model_space (H : Type u_1) [topological_space H] (G : structure_groupoid H) : has_groupoid H G := sorry

/-- Any charted space structure is compatible with the groupoid of all local homeomorphisms -/
protected instance has_groupoid_continuous_groupoid {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] : has_groupoid M (continuous_groupoid H) :=
  has_groupoid.mk
    fun (e e' : local_homeomorph M H) (he : e ∈ charted_space.atlas H M) (he' : e' ∈ charted_space.atlas H M) =>
      eq.mpr
        (id
          (Eq._oldrec (Eq.refl (local_homeomorph.trans (local_homeomorph.symm e) e' ∈ continuous_groupoid H))
            (continuous_groupoid.equations._eqn_1 H)))
        (eq.mpr
          (id
            (Eq._oldrec
              (Eq.refl
                (local_homeomorph.trans (local_homeomorph.symm e) e' ∈ pregroupoid.groupoid (continuous_pregroupoid H)))
              (propext mem_groupoid_of_pregroupoid)))
          (eq.mpr (id (propext (and_self True))) trivial))

/-- Given a charted space admitting a structure groupoid, the maximal atlas associated to this
structure groupoid is the set of all local charts that are compatible with the atlas, i.e., such
that changing coordinates with an atlas member gives an element of the groupoid. -/
def structure_groupoid.maximal_atlas {H : Type u} (M : Type u_2) [topological_space H] [topological_space M] [charted_space H M] (G : structure_groupoid H) : set (local_homeomorph M H) :=
  set_of
    fun (e : local_homeomorph M H) =>
      ∀ (e' : local_homeomorph M H),
        e' ∈ charted_space.atlas H M →
          local_homeomorph.trans (local_homeomorph.symm e) e' ∈ G ∧
            local_homeomorph.trans (local_homeomorph.symm e') e ∈ G

/-- The elements of the atlas belong to the maximal atlas for any structure groupoid -/
theorem structure_groupoid.mem_maximal_atlas_of_mem_atlas {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] (G : structure_groupoid H) [has_groupoid M G] {e : local_homeomorph M H} (he : e ∈ charted_space.atlas H M) : e ∈ structure_groupoid.maximal_atlas M G :=
  fun (e' : local_homeomorph M H) (he' : e' ∈ charted_space.atlas H M) =>
    { left := structure_groupoid.compatible G he he', right := structure_groupoid.compatible G he' he }

theorem structure_groupoid.chart_mem_maximal_atlas {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] (G : structure_groupoid H) [has_groupoid M G] (x : M) : charted_space.chart_at H x ∈ structure_groupoid.maximal_atlas M G :=
  structure_groupoid.mem_maximal_atlas_of_mem_atlas G (charted_space.chart_mem_atlas H x)

theorem mem_maximal_atlas_iff {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] {G : structure_groupoid H} {e : local_homeomorph M H} : e ∈ structure_groupoid.maximal_atlas M G ↔
  ∀ (e' : local_homeomorph M H),
    e' ∈ charted_space.atlas H M →
      local_homeomorph.trans (local_homeomorph.symm e) e' ∈ G ∧ local_homeomorph.trans (local_homeomorph.symm e') e ∈ G :=
  iff.rfl

/-- Changing coordinates between two elements of the maximal atlas gives rise to an element
of the structure groupoid. -/
theorem structure_groupoid.compatible_of_mem_maximal_atlas {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] {G : structure_groupoid H} {e : local_homeomorph M H} {e' : local_homeomorph M H} (he : e ∈ structure_groupoid.maximal_atlas M G) (he' : e' ∈ structure_groupoid.maximal_atlas M G) : local_homeomorph.trans (local_homeomorph.symm e) e' ∈ G := sorry

/-- In the model space, the identity is in any maximal atlas. -/
theorem structure_groupoid.id_mem_maximal_atlas {H : Type u} [topological_space H] (G : structure_groupoid H) : local_homeomorph.refl H ∈ structure_groupoid.maximal_atlas H G :=
  structure_groupoid.mem_maximal_atlas_of_mem_atlas G
    (eq.mpr (id (Eq.trans (propext charted_space_self_atlas) (propext (eq_self_iff_true (local_homeomorph.refl H)))))
      trivial)

/-- If a single local homeomorphism `e` from a space `α` into `H` has source covering the whole
space `α`, then that local homeomorphism induces an `H`-charted space structure on `α`.
(This condition is equivalent to `e` being an open embedding of `α` into `H`; see
`local_homeomorph.to_open_embedding` and `open_embedding.to_local_homeomorph`.) -/
def singleton_charted_space {H : Type u} [topological_space H] {α : Type u_5} [topological_space α] (e : local_homeomorph α H) (h : local_equiv.source (local_homeomorph.to_local_equiv e) = set.univ) : charted_space H α :=
  charted_space.mk (singleton e) (fun (_x : α) => e) sorry sorry

theorem singleton_charted_space_one_chart {H : Type u} [topological_space H] {α : Type u_5} [topological_space α] (e : local_homeomorph α H) (h : local_equiv.source (local_homeomorph.to_local_equiv e) = set.univ) (e' : local_homeomorph α H) (h' : e' ∈ charted_space.atlas H α) : e' = e :=
  h'

/-- Given a local homeomorphism `e` from a space `α` into `H`, if its source covers the whole
space `α`, then the induced charted space structure on `α` is `has_groupoid G` for any structure
groupoid `G` which is closed under restrictions. -/
theorem singleton_has_groupoid {H : Type u} [topological_space H] {α : Type u_5} [topological_space α] (e : local_homeomorph α H) (h : local_equiv.source (local_homeomorph.to_local_equiv e) = set.univ) (G : structure_groupoid H) [closed_under_restriction G] : has_groupoid α G := sorry

namespace topological_space.opens

/-- An open subset of a charted space is naturally a charted space. -/
protected instance charted_space {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] (s : opens M) : charted_space H ↥s :=
  charted_space.mk
    (set.Union fun (x : ↥s) => singleton (local_homeomorph.subtype_restr (charted_space.chart_at H (subtype.val x)) s))
    (fun (x : ↥s) => local_homeomorph.subtype_restr (charted_space.chart_at H (subtype.val x)) s) sorry sorry

/-- If a groupoid `G` is `closed_under_restriction`, then an open subset of a space which is
`has_groupoid G` is naturally `has_groupoid G`. -/
protected instance has_groupoid {H : Type u} {M : Type u_2} [topological_space H] [topological_space M] [charted_space H M] (G : structure_groupoid H) [has_groupoid M G] (s : opens M) [closed_under_restriction G] : has_groupoid (↥s) G := sorry

end topological_space.opens

/-! ### Structomorphisms -/

/-- A `G`-diffeomorphism between two charted spaces is a homeomorphism which, when read in the
charts, belongs to `G`. We avoid the word diffeomorph as it is too related to the smooth category,
and use structomorph instead. -/
structure structomorph {H : Type u} [topological_space H] (G : structure_groupoid H) (M : Type u_5) (M' : Type u_6) [topological_space M] [topological_space M'] [charted_space H M] [charted_space H M']
extends M ≃ₜ M'
where
  mem_groupoid : ∀ (c : local_homeomorph M H) (c' : local_homeomorph M' H),
  c ∈ charted_space.atlas H M →
    c' ∈ charted_space.atlas H M' →
      local_homeomorph.trans (local_homeomorph.symm c)
          (local_homeomorph.trans (homeomorph.to_local_homeomorph _to_homeomorph) c') ∈
        G

/-- The identity is a diffeomorphism of any charted space, for any groupoid. -/
def structomorph.refl {H : Type u} [topological_space H] {G : structure_groupoid H} (M : Type u_1) [topological_space M] [charted_space H M] [has_groupoid M G] : structomorph G M M :=
  structomorph.mk (homeomorph.mk (homeomorph.to_equiv (homeomorph.refl M))) sorry

/-- The inverse of a structomorphism is a structomorphism -/
def structomorph.symm {H : Type u} {M : Type u_2} {M' : Type u_3} [topological_space H] [topological_space M] [charted_space H M] [topological_space M'] {G : structure_groupoid H} [charted_space H M'] (e : structomorph G M M') : structomorph G M' M :=
  structomorph.mk (homeomorph.mk (homeomorph.to_equiv (homeomorph.symm (structomorph.to_homeomorph e)))) sorry

/-- The composition of structomorphisms is a structomorphism -/
def structomorph.trans {H : Type u} {M : Type u_2} {M' : Type u_3} {M'' : Type u_4} [topological_space H] [topological_space M] [charted_space H M] [topological_space M'] [topological_space M''] {G : structure_groupoid H} [charted_space H M'] [charted_space H M''] (e : structomorph G M M') (e' : structomorph G M' M'') : structomorph G M M'' :=
  structomorph.mk
    (homeomorph.mk
      (homeomorph.to_equiv (homeomorph.trans (structomorph.to_homeomorph e) (structomorph.to_homeomorph e'))))
    sorry


import data.finset.basic tactic.norm_num data.nat.nth

/-! # IMO 2021 A1 -/

namespace IMOSL
namespace IMO2021A1

private lemma lem1 {n : ℕ} (hn : 0 < n)
  {x : ℕ → ℕ} (h : strict_mono x) (h0 : x (4 * n + 1) ≤ 5 ^ n) :
  ∃ i : ℕ, i < 4 * n ∧ 3 * x i.succ < x (4 * n + 1) + 2 * x i :=
begin
  contrapose! h0,
  let y : ℕ → ℕ := λ i, x (4 * n + 1) - x (4 * n - i),
  suffices : ∀ i : ℕ, i ≤ 4 * n → 3 ^ i ≤ 2 ^ i * y i,
  { replace this := this (4 * n) (le_refl _),
    rw [pow_mul, pow_mul] at this,
    replace this : (5 * 2 ^ 4) ^ n < (2 ^ 4) ^ n * y (4 * n) :=
      lt_of_lt_of_le (nat.pow_lt_pow_of_lt_left (by norm_num) hn) this,
    dsimp only [y] at this,
    rw [mul_pow, mul_comm, mul_lt_mul_left, nat.sub_self] at this,
    exacts [lt_of_lt_of_le this (nat.sub_le _ _), pow_pos (pow_pos two_pos 4) n] },
  intros i; induction i with i i_ih,
  { rintros -; dsimp only [y],
    rw [pow_zero, pow_zero, one_mul, nat.sub_zero, nat.succ_le_iff, tsub_pos_iff_lt],
    exact h (lt_add_one (4 * n)) },
  { intros h1,
    replace i_ih := i_ih (le_trans i.le_succ h1),
    rw [pow_succ', pow_succ'],
    refine le_trans (mul_le_mul_right' i_ih _) _,
    rw [mul_assoc, mul_assoc, mul_le_mul_left, mul_comm, ← not_lt],
    work_on_goal 2 { exact pow_pos two_pos i },

    clear i_ih; intros h2,
    replace h2 := add_lt_add_of_lt_of_le h2
      (h0 (4 * n - i.succ) (i.succ.sub_lt_of_pos_le _ i.succ_pos h1)),
    rw [← nat.succ_sub h1, nat.succ_sub_succ, ← mul_add, add_left_comm, ← mul_add] at h2,
    dsimp only [y] at h2,
    rw [nat.sub_add_cancel, nat.sub_add_cancel, ← one_add_mul, ← not_le] at h2,
    exact h2 (le_refl _),
    all_goals { refine h.monotone (le_trans (nat.sub_le _ _) (4 * n).le_succ) } }
end

private lemma lem2 {A : finset ℕ} {n : ℕ} (h : A.card = 4 * n + 2) : A.nonempty :=
  by rw [← finset.card_pos, h]; exact nat.add_pos_right _ two_pos

section strict_map

variables {A : finset ℕ} (h : A.nonempty)
include h

private noncomputable def finset_to_strict_map (i : ℕ) : ℕ :=
  ite (i < A.card) (nat.nth (λ a, a ∈ A) i) (A.max' h + i)

private lemma strict_map_mem {i : ℕ} (h0 : i < A.card) : finset_to_strict_map h i ∈ A :=
begin
  rw [finset_to_strict_map, if_pos h0],
  have h1 := A.finite_to_set,
  refine nat.nth_mem_of_lt_card_finite (λ a, a ∈ A) h1 _,
  rwa A.finite_to_set_to_finset
end

private lemma strict_map_le_max {i : ℕ} (h0 : i < A.card) : finset_to_strict_map h i ≤ A.max' h :=
  A.le_max' _ (strict_map_mem h h0)

private lemma strict_map_strict_mono : strict_mono (finset_to_strict_map h) :=
begin
  intros i j h; unfold finset_to_strict_map,
  cases le_or_lt A.card i with h0 h0,
  rwa [if_neg (not_lt_of_le (le_trans h0 (le_of_lt h))),
       if_neg (not_lt_of_le h0), add_lt_add_iff_left],
  have h2 := A.finite_to_set,
  rw if_pos h0; cases lt_or_le j A.card with h1 h1,
  rw if_pos h1; exact nat.nth_strict_mono_of_finite _ h2 (by rwa A.finite_to_set_to_finset) h,
  rw if_neg (not_lt_of_le h1),
  refine lt_add_of_le_of_pos
    (A.le_max' _ (nat.nth_mem_of_lt_card_finite (λ a, a ∈ A) h2 _)) (pos_of_gt h),
  rwa A.finite_to_set_to_finset
end

private lemma strict_map_image_of_mem {a : ℕ} (h0 : a ∈ A) :
  ∃ i : ℕ, i < A.card ∧ a = finset_to_strict_map h i :=
begin
  have h1 : {x | x ∈ A}.finite := {x : ℕ | x ∈ A}.to_finite,
  have h2 : nat.count (λ x, x ∈ A) a < A.card :=
    by convert nat.count_lt_card h1 h0; exact A.finite_to_set_to_finset.symm,
  refine ⟨nat.count (λ x, x ∈ A) a, h2, _⟩,
  rw [finset_to_strict_map, if_pos h2, nat.nth_count (λ x, x ∈ A) h0]
end

private lemma strict_map_max : finset_to_strict_map h (A.card - 1) = A.max' h :=
begin
  refine le_antisymm (strict_map_le_max h (nat.sub_lt (finset.card_pos.mpr h) one_pos)) _,
  rw finset.max'_le_iff; intros y h0,
  rcases strict_map_image_of_mem h h0 with ⟨i, h1, rfl⟩,
  exact (strict_map_strict_mono h).monotone (nat.le_pred_of_lt h1)
end

end strict_map

/-- Final solution -/
theorem final_solution {n : ℕ} (hn : 0 < n)
  {A : finset ℕ} (h : A.card = 4 * n + 2) (h0 : A.max' (lem2 h) ≤ 5 ^ n) :
  ∃ a b c : ℕ, a ∈ A ∧ b ∈ A ∧ c ∈ A ∧ a < b ∧ b < c ∧ 3 * b < c + 2 * a :=
begin
  let X := lem2 h,
  obtain ⟨i, h1, h2⟩ := lem1 hn (strict_map_strict_mono X)
    (by convert h0; rw [← strict_map_max, h, nat.add_succ_sub_one]),
  have h3 := nat.succ_lt_succ h1,
  have h4 : 4 * n + 1 < A.card := by rw h; exact (4 * n + 1).lt_succ_self,
  have h5 := lt_trans h3 h4,
  exact ⟨_, _, _, strict_map_mem _ (lt_trans i.lt_succ_self h5), strict_map_mem _ h5,
    strict_map_mem _ h4, strict_map_strict_mono _ i.lt_succ_self, strict_map_strict_mono _ h3, h2⟩
end

end IMO2021A1
end IMOSL


/-
Copyright (c) 2019 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import ring_theory.finiteness
import linear_algebra.dimension

/-!
# A module over a division ring is noetherian if and only if it is finite.

-/

universes u v

open_locale classical cardinal
open cardinal submodule module function

namespace is_noetherian

variables {K : Type u} {V : Type v} [division_ring K] [add_comm_group V] [module K V]

/--
A module over a division ring is noetherian if and only if
its dimension (as a cardinal) is strictly less than the first infinite cardinal `ℵ₀`.
-/
lemma iff_dim_lt_aleph_0 : is_noetherian K V ↔ module.rank K V < ℵ₀ :=
begin
  let b := basis.of_vector_space K V,
  rw [← b.mk_eq_dim'', lt_aleph_0_iff_set_finite],
  split,
  { introI,
    exact finite_of_linear_independent (basis.of_vector_space_index.linear_independent K V) },
  { assume hbfinite,
    refine @is_noetherian_of_linear_equiv K (⊤ : submodule K V) V _
      _ _ _ _ (linear_equiv.of_top _ rfl) (id _),
    refine is_noetherian_of_fg_of_noetherian _ ⟨set.finite.to_finset hbfinite, _⟩,
    rw [set.finite.coe_to_finset, ← b.span_eq, basis.coe_of_vector_space, subtype.range_coe] }
end

variables (K V)

/-- The dimension of a noetherian module over a division ring, as a cardinal,
is strictly less than the first infinite cardinal `ℵ₀`. -/
lemma dim_lt_aleph_0 : ∀ [is_noetherian K V], module.rank K V < ℵ₀ :=
is_noetherian.iff_dim_lt_aleph_0.1

variables {K V}

/-- In a noetherian module over a division ring, all bases are indexed by a finite type. -/
noncomputable def fintype_basis_index {ι : Type*} [is_noetherian K V] (b : basis ι K V) :
  fintype ι :=
b.fintype_index_of_dim_lt_aleph_0 (dim_lt_aleph_0 K V)

/-- In a noetherian module over a division ring,
`basis.of_vector_space` is indexed by a finite type. -/
noncomputable instance [is_noetherian K V] : fintype (basis.of_vector_space_index K V) :=
fintype_basis_index (basis.of_vector_space K V)

/-- In a noetherian module over a division ring,
if a basis is indexed by a set, that set is finite. -/
lemma finite_basis_index {ι : Type*} {s : set ι} [is_noetherian K V] (b : basis s K V) :
  s.finite :=
b.finite_index_of_dim_lt_aleph_0 (dim_lt_aleph_0 K V)

variables (K V)

/-- In a noetherian module over a division ring,
there exists a finite basis. This is the indexing `finset`. -/
noncomputable def finset_basis_index [is_noetherian K V] :
  finset V :=
(finite_basis_index (basis.of_vector_space K V)).to_finset

@[simp] lemma coe_finset_basis_index [is_noetherian K V] :
  (↑(finset_basis_index K V) : set V) = basis.of_vector_space_index K V :=
set.finite.coe_to_finset _

@[simp] lemma coe_sort_finset_basis_index [is_noetherian K V] :
  ((finset_basis_index K V) : Type*) = basis.of_vector_space_index K V :=
set.finite.coe_sort_to_finset _

/--
In a noetherian module over a division ring, there exists a finite basis.
This is indexed by the `finset` `finite_dimensional.finset_basis_index`.
This is in contrast to the result `finite_basis_index (basis.of_vector_space K V)`,
which provides a set and a `set.finite`.
-/
noncomputable def finset_basis [is_noetherian K V] :
  basis (finset_basis_index K V) K V :=
(basis.of_vector_space K V).reindex (by simp)

@[simp] lemma range_finset_basis [is_noetherian K V] :
  set.range (finset_basis K V) = basis.of_vector_space_index K V :=
by rw [finset_basis, basis.range_reindex, basis.range_of_vector_space]

variables {K V}

/-- A module over a division ring is noetherian if and only if it is finitely generated. -/
lemma iff_fg :
  is_noetherian K V ↔ module.finite K V :=
begin
  split,
  { introI h,
    exact ⟨⟨finset_basis_index K V, by { convert (finset_basis K V).span_eq, simp }⟩⟩ },
  { rintros ⟨s, hs⟩,
    rw [is_noetherian.iff_dim_lt_aleph_0, ← dim_top, ← hs],
    exact lt_of_le_of_lt (dim_span_le _) s.finite_to_set.lt_aleph_0 }
end

end is_noetherian


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

! This file was ported from Lean 3 source module order.partition.equipartition
! leanprover-community/mathlib commit 0a0ec35061ed9960bf0e7ffb0335f44447b58977
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Equitable
import Mathbin.Order.Partition.Finpartition

/-!
# Finite equipartitions

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

This file defines finite equipartitions, the partitions whose parts all are the same size up to a
difference of `1`.

## Main declarations

* `finpartition.is_equipartition`: Predicate for a `finpartition` to be an equipartition.
-/

open Finset Fintype

namespace Finpartition

variable {α : Type _} [DecidableEq α] {s t : Finset α} (P : Finpartition s)

/- warning: finpartition.is_equipartition -> Finpartition.IsEquipartition is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α}, (Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s) -> Prop
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α}, (Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s) -> Prop
Case conversion may be inaccurate. Consider using '#align finpartition.is_equipartition Finpartition.IsEquipartitionₓ'. -/
/-- An equipartition is a partition whose parts are all the same size, up to a difference of `1`. -/
def IsEquipartition : Prop :=
  (P.parts : Set (Finset α)).EquitableOn card
#align finpartition.is_equipartition Finpartition.IsEquipartition

/- warning: finpartition.is_equipartition_iff_card_parts_eq_average -> Finpartition.isEquipartition_iff_card_parts_eq_average is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} (P : Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s), Iff (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) (forall (a : Finset.{u1} α), (Membership.Mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.hasMem.{u1} (Finset.{u1} α)) a (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P)) -> (Or (Eq.{1} Nat (Finset.card.{u1} α a) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P)))) (Eq.{1} Nat (Finset.card.{u1} α a) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} (P : Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s), Iff (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) (forall (a : Finset.{u1} α), (Membership.mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.instMembershipFinset.{u1} (Finset.{u1} α)) a (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P)) -> (Or (Eq.{1} Nat (Finset.card.{u1} α a) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P)))) (Eq.{1} Nat (Finset.card.{u1} α a) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))))
Case conversion may be inaccurate. Consider using '#align finpartition.is_equipartition_iff_card_parts_eq_average Finpartition.isEquipartition_iff_card_parts_eq_averageₓ'. -/
theorem isEquipartition_iff_card_parts_eq_average :
    P.IsEquipartition ↔
      ∀ a : Finset α,
        a ∈ P.parts → a.card = s.card / P.parts.card ∨ a.card = s.card / P.parts.card + 1 :=
  by simp_rw [is_equipartition, Finset.equitableOn_iff, P.sum_card_parts]
#align finpartition.is_equipartition_iff_card_parts_eq_average Finpartition.isEquipartition_iff_card_parts_eq_average

variable {P}

theorem Set.Subsingleton.isEquipartition (h : (P.parts : Set (Finset α)).Subsingleton) :
    P.IsEquipartition :=
  h.EquitableOn _
#align set.subsingleton.is_equipartition Set.Subsingleton.isEquipartition

/- warning: finpartition.is_equipartition.card_parts_eq_average -> Finpartition.IsEquipartition.card_parts_eq_average is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {t : Finset.{u1} α} {P : Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s}, (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) -> (Membership.Mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.hasMem.{u1} (Finset.{u1} α)) t (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P)) -> (Or (Eq.{1} Nat (Finset.card.{u1} α t) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P)))) (Eq.{1} Nat (Finset.card.{u1} α t) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {t : Finset.{u1} α} {P : Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s}, (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) -> (Membership.mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.instMembershipFinset.{u1} (Finset.{u1} α)) t (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P)) -> (Or (Eq.{1} Nat (Finset.card.{u1} α t) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P)))) (Eq.{1} Nat (Finset.card.{u1} α t) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))
Case conversion may be inaccurate. Consider using '#align finpartition.is_equipartition.card_parts_eq_average Finpartition.IsEquipartition.card_parts_eq_averageₓ'. -/
theorem IsEquipartition.card_parts_eq_average (hP : P.IsEquipartition) (ht : t ∈ P.parts) :
    t.card = s.card / P.parts.card ∨ t.card = s.card / P.parts.card + 1 :=
  P.isEquipartition_iff_card_parts_eq_average.1 hP _ ht
#align finpartition.is_equipartition.card_parts_eq_average Finpartition.IsEquipartition.card_parts_eq_average

/- warning: finpartition.is_equipartition.average_le_card_part -> Finpartition.IsEquipartition.average_le_card_part is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {t : Finset.{u1} α} {P : Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s}, (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) -> (Membership.Mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.hasMem.{u1} (Finset.{u1} α)) t (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P)) -> (LE.le.{0} Nat Nat.hasLe (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P))) (Finset.card.{u1} α t))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {t : Finset.{u1} α} {P : Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s}, (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) -> (Membership.mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.instMembershipFinset.{u1} (Finset.{u1} α)) t (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P)) -> (LE.le.{0} Nat instLENat (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P))) (Finset.card.{u1} α t))
Case conversion may be inaccurate. Consider using '#align finpartition.is_equipartition.average_le_card_part Finpartition.IsEquipartition.average_le_card_partₓ'. -/
theorem IsEquipartition.average_le_card_part (hP : P.IsEquipartition) (ht : t ∈ P.parts) :
    s.card / P.parts.card ≤ t.card := by
  rw [← P.sum_card_parts]
  exact equitable_on.le hP ht
#align finpartition.is_equipartition.average_le_card_part Finpartition.IsEquipartition.average_le_card_part

/- warning: finpartition.is_equipartition.card_part_le_average_add_one -> Finpartition.IsEquipartition.card_part_le_average_add_one is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {t : Finset.{u1} α} {P : Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s}, (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) -> (Membership.Mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.hasMem.{u1} (Finset.{u1} α)) t (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P)) -> (LE.le.{0} Nat Nat.hasLe (Finset.card.{u1} α t) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s P))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {t : Finset.{u1} α} {P : Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s}, (Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s P) -> (Membership.mem.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Finset.{u1} α)) (Finset.instMembershipFinset.{u1} (Finset.{u1} α)) t (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P)) -> (LE.le.{0} Nat instLENat (Finset.card.{u1} α t) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (Finset.card.{u1} α s) (Finset.card.{u1} (Finset.{u1} α) (Finpartition.parts.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s P))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))
Case conversion may be inaccurate. Consider using '#align finpartition.is_equipartition.card_part_le_average_add_one Finpartition.IsEquipartition.card_part_le_average_add_oneₓ'. -/
theorem IsEquipartition.card_part_le_average_add_one (hP : P.IsEquipartition) (ht : t ∈ P.parts) :
    t.card ≤ s.card / P.parts.card + 1 :=
  by
  rw [← P.sum_card_parts]
  exact equitable_on.le_add_one hP ht
#align finpartition.is_equipartition.card_part_le_average_add_one Finpartition.IsEquipartition.card_part_le_average_add_one

/-! ### Discrete and indiscrete finpartition -/

variable (s)

#print Finpartition.bot_isEquipartition /-
theorem bot_isEquipartition : (⊥ : Finpartition s).IsEquipartition :=
  Set.equitableOn_iff_exists_eq_eq_add_one.2 ⟨1, by simp⟩
#align finpartition.bot_is_equipartition Finpartition.bot_isEquipartition
-/

/- warning: finpartition.top_is_equipartition -> Finpartition.top_isEquipartition is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Finset.{u1} α), Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s (Top.top.{u1} (Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s) (OrderTop.toHasTop.{u1} (Finpartition.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s) (Finpartition.hasLe.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s) (Finpartition.orderTop.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.orderBot.{u1} α) s (Finset.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s (Bot.bot.{u1} (Finset.{u1} α) (OrderBot.toHasBot.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (SemilatticeInf.toPartialOrder.{u1} (Finset.{u1} α) (Lattice.toSemilatticeInf.{u1} (Finset.{u1} α) (Finset.lattice.{u1} α (fun (a : α) (b : α) => _inst_1 a b)))))) (Finset.orderBot.{u1} α)))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Finset.{u1} α) [inst._@.Mathlib.Order.Partition.Equipartition._hyg.374 : Decidable (Eq.{succ u1} (Finset.{u1} α) s (Bot.bot.{u1} (Finset.{u1} α) (GeneralizedBooleanAlgebra.toBot.{u1} (Finset.{u1} α) (Finset.instGeneralizedBooleanAlgebraFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)))))], Finpartition.IsEquipartition.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s (Top.top.{u1} (Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s) (OrderTop.toTop.{u1} (Finpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s) (Finpartition.instLEFinpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s) (Finpartition.instOrderTopFinpartitionInstLEFinpartition.{u1} (Finset.{u1} α) (Finset.instLatticeFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{u1} α) s inst._@.Mathlib.Order.Partition.Equipartition._hyg.374)))
Case conversion may be inaccurate. Consider using '#align finpartition.top_is_equipartition Finpartition.top_isEquipartitionₓ'. -/
theorem top_isEquipartition : (⊤ : Finpartition s).IsEquipartition :=
  (parts_top_subsingleton _).IsEquipartition
#align finpartition.top_is_equipartition Finpartition.top_isEquipartition

#print Finpartition.indiscrete_isEquipartition /-
theorem indiscrete_isEquipartition {hs : s ≠ ∅} : (indiscrete hs).IsEquipartition :=
  by
  rw [is_equipartition, indiscrete_parts, coe_singleton]
  exact Set.equitableOn_singleton s _
#align finpartition.indiscrete_is_equipartition Finpartition.indiscrete_isEquipartition
-/

end Finpartition


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

! This file was ported from Lean 3 source module data.pnat.basic
! leanprover-community/mathlib commit ba2245edf0c8bb155f1569fd9b9492a9b384cde6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.PNat.Defs
import Mathlib.Data.Nat.Bits
import Mathlib.Data.Nat.Order.Basic
import Mathlib.Data.Set.Basic
import Mathlib.Algebra.GroupWithZero.Divisibility
import Mathlib.Algebra.Order.Positive.Ring

/-!
# The positive natural numbers

This file develops the type `ℕ+` or `PNat`, the subtype of natural numbers that are positive.
It is defined in `Data.PNat.Defs`, but most of the development is deferred to here so
that `Data.PNat.Defs` can have very few imports.
-/

deriving instance AddLeftCancelSemigroup, AddRightCancelSemigroup, AddCommSemigroup,
  LinearOrderedCancelCommMonoid, Add, Mul, Distrib for PNat

namespace PNat

@[simp]
theorem one_add_natPred (n : ℕ+) : 1 + n.natPred = n := by
  rw [natPred, add_tsub_cancel_iff_le.mpr <| show 1 ≤ (n : ℕ) from n.2]
#align pnat.one_add_nat_pred PNat.one_add_natPred

@[simp]
theorem natPred_add_one (n : ℕ+) : n.natPred + 1 = n :=
  (add_comm _ _).trans n.one_add_natPred
#align pnat.nat_pred_add_one PNat.natPred_add_one

@[mono]
theorem natPred_strictMono : StrictMono natPred := fun m _ h => Nat.pred_lt_pred m.2.ne' h
#align pnat.nat_pred_strict_mono PNat.natPred_strictMono

@[mono]
theorem natPred_monotone : Monotone natPred :=
  natPred_strictMono.monotone
#align pnat.nat_pred_monotone PNat.natPred_monotone

theorem natPred_injective : Function.Injective natPred :=
  natPred_strictMono.injective
#align pnat.nat_pred_injective PNat.natPred_injective

@[simp]
theorem natPred_lt_natPred {m n : ℕ+} : m.natPred < n.natPred ↔ m < n :=
  natPred_strictMono.lt_iff_lt
#align pnat.nat_pred_lt_nat_pred PNat.natPred_lt_natPred

@[simp]
theorem natPred_le_natPred {m n : ℕ+} : m.natPred ≤ n.natPred ↔ m ≤ n :=
  natPred_strictMono.le_iff_le
#align pnat.nat_pred_le_nat_pred PNat.natPred_le_natPred

@[simp]
theorem natPred_inj {m n : ℕ+} : m.natPred = n.natPred ↔ m = n :=
  natPred_injective.eq_iff
#align pnat.nat_pred_inj PNat.natPred_inj

end PNat

namespace Nat

@[mono]
theorem succPNat_strictMono : StrictMono succPNat := fun _ _ => Nat.succ_lt_succ
#align nat.succ_pnat_strict_mono Nat.succPNat_strictMono

@[mono]
theorem succPNat_mono : Monotone succPNat :=
  succPNat_strictMono.monotone
#align nat.succ_pnat_mono Nat.succPNat_mono

@[simp]
theorem succPNat_lt_succPNat {m n : ℕ} : m.succPNat < n.succPNat ↔ m < n :=
  succPNat_strictMono.lt_iff_lt
#align nat.succ_pnat_lt_succ_pnat Nat.succPNat_lt_succPNat

@[simp]
theorem succPNat_le_succPNat {m n : ℕ} : m.succPNat ≤ n.succPNat ↔ m ≤ n :=
  succPNat_strictMono.le_iff_le
#align nat.succ_pnat_le_succ_pnat Nat.succPNat_le_succPNat

theorem succPNat_injective : Function.Injective succPNat :=
  succPNat_strictMono.injective
#align nat.succ_pnat_injective Nat.succPNat_injective

@[simp]
theorem succPNat_inj {n m : ℕ} : succPNat n = succPNat m ↔ n = m :=
  succPNat_injective.eq_iff
#align nat.succ_pnat_inj Nat.succPNat_inj

end Nat

namespace PNat

open Nat

/-- We now define a long list of structures on `ℕ+` induced by
 similar structures on `ℕ`. Most of these behave in a completely
 obvious way, but there are a few things to be said about
 subtraction, division and powers.
-/
@[simp, norm_cast]
theorem coe_inj {m n : ℕ+} : (m : ℕ) = n ↔ m = n :=
  SetCoe.ext_iff
#align pnat.coe_inj PNat.coe_inj

@[simp, norm_cast]
theorem add_coe (m n : ℕ+) : ((m + n : ℕ+) : ℕ) = m + n :=
  rfl
#align pnat.add_coe PNat.add_coe

/-- `coe` promoted to an `AddHom`, that is, a morphism which preserves addition. -/
def coeAddHom : AddHom ℕ+ ℕ where
  toFun := Coe.coe
  map_add' := add_coe
#align pnat.coe_add_hom PNat.coeAddHom

instance covariantClass_add_le : CovariantClass ℕ+ ℕ+ (· + ·) (· ≤ ·) :=
  Positive.covariantClass_add_le

instance covariantClass_add_lt : CovariantClass ℕ+ ℕ+ (· + ·) (· < ·) :=
  Positive.covariantClass_add_lt

instance contravariantClass_add_le : ContravariantClass ℕ+ ℕ+ (· + ·) (· ≤ ·) :=
  Positive.contravariantClass_add_le

instance contravariantClass_add_lt : ContravariantClass ℕ+ ℕ+ (· + ·) (· < ·) :=
  Positive.contravariantClass_add_lt

/-- An equivalence between `ℕ+` and `ℕ` given by `PNat.natPred` and `Nat.succPNat`. -/
@[simps (config := { fullyApplied := false })]
def _root_.Equiv.pnatEquivNat : ℕ+ ≃ ℕ where
  toFun := PNat.natPred
  invFun := Nat.succPNat
  left_inv := succPNat_natPred
  right_inv := Nat.natPred_succPNat
#align equiv.pnat_equiv_nat Equiv.pnatEquivNat
#align equiv.pnat_equiv_nat_symm_apply Equiv.pnatEquivNat_symm_apply
#align equiv.pnat_equiv_nat_apply Equiv.pnatEquivNat_apply

/-- The order isomorphism between ℕ and ℕ+ given by `succ`. -/
@[simps! (config := { fullyApplied := false }) apply]
def _root_.OrderIso.pnatIsoNat : ℕ+ ≃o ℕ where
  toEquiv := Equiv.pnatEquivNat
  map_rel_iff' := natPred_le_natPred
#align order_iso.pnat_iso_nat OrderIso.pnatIsoNat
#align order_iso.pnat_iso_nat_apply OrderIso.pnatIsoNat_apply

@[simp]
theorem _root_.OrderIso.pnatIsoNat_symm_apply : OrderIso.pnatIsoNat.symm = Nat.succPNat :=
  rfl
#align order_iso.pnat_iso_nat_symm_apply OrderIso.pnatIsoNat_symm_apply

theorem lt_add_one_iff : ∀ {a b : ℕ+}, a < b + 1 ↔ a ≤ b := Nat.lt_add_one_iff
#align pnat.lt_add_one_iff PNat.lt_add_one_iff

theorem add_one_le_iff : ∀ {a b : ℕ+}, a + 1 ≤ b ↔ a < b := Nat.add_one_le_iff
#align pnat.add_one_le_iff PNat.add_one_le_iff

instance : OrderBot ℕ+ where
  bot := 1
  bot_le a := a.property

@[simp]
theorem bot_eq_one : (⊥ : ℕ+) = 1 :=
  rfl
#align pnat.bot_eq_one PNat.bot_eq_one

-- Porting note: deprecated
section deprecated

set_option linter.deprecated false

-- Some lemmas that rewrite `PNat.mk n h`, for `n` an explicit numeral, into explicit numerals.
@[simp, deprecated]
theorem mk_bit0 (n) {h} : (⟨bit0 n, h⟩ : ℕ+) = (bit0 ⟨n, pos_of_bit0_pos h⟩ : ℕ+) :=
  rfl
#align pnat.mk_bit0 PNat.mk_bit0

@[simp, deprecated]
theorem mk_bit1 (n) {h} {k} : (⟨bit1 n, h⟩ : ℕ+) = (bit1 ⟨n, k⟩ : ℕ+) :=
  rfl
#align pnat.mk_bit1 PNat.mk_bit1

-- Some lemmas that rewrite inequalities between explicit numerals in `ℕ+`
-- into the corresponding inequalities in `ℕ`.
-- TODO: perhaps this should not be attempted by `simp`,
-- and instead we should expect `norm_num` to take care of these directly?
-- TODO: these lemmas are perhaps incomplete:
-- * 1 is not represented as a bit0 or bit1
-- * strict inequalities?
@[simp, deprecated]
theorem bit0_le_bit0 (n m : ℕ+) : bit0 n ≤ bit0 m ↔ bit0 (n : ℕ) ≤ bit0 (m : ℕ) :=
  Iff.rfl
#align pnat.bit0_le_bit0 PNat.bit0_le_bit0

@[simp, deprecated]
theorem bit0_le_bit1 (n m : ℕ+) : bit0 n ≤ bit1 m ↔ bit0 (n : ℕ) ≤ bit1 (m : ℕ) :=
  Iff.rfl
#align pnat.bit0_le_bit1 PNat.bit0_le_bit1

@[simp, deprecated]
theorem bit1_le_bit0 (n m : ℕ+) : bit1 n ≤ bit0 m ↔ bit1 (n : ℕ) ≤ bit0 (m : ℕ) :=
  Iff.rfl
#align pnat.bit1_le_bit0 PNat.bit1_le_bit0

@[simp, deprecated]
theorem bit1_le_bit1 (n m : ℕ+) : bit1 n ≤ bit1 m ↔ bit1 (n : ℕ) ≤ bit1 (m : ℕ) :=
  Iff.rfl
#align pnat.bit1_le_bit1 PNat.bit1_le_bit1

end deprecated

@[simp, norm_cast]
theorem mul_coe (m n : ℕ+) : ((m * n : ℕ+) : ℕ) = m * n :=
  rfl
#align pnat.mul_coe PNat.mul_coe

/-- `PNat.coe` promoted to a `MonoidHom`. -/
def coeMonoidHom : ℕ+ →* ℕ where
  toFun := Coe.coe
  map_one' := one_coe
  map_mul' := mul_coe
#align pnat.coe_monoid_hom PNat.coeMonoidHom

@[simp]
theorem coe_coeMonoidHom : (coeMonoidHom : ℕ+ → ℕ) = Coe.coe :=
  rfl
#align pnat.coe_coe_monoid_hom PNat.coe_coeMonoidHom

@[simp]
theorem le_one_iff {n : ℕ+} : n ≤ 1 ↔ n = 1 :=
  le_bot_iff
#align pnat.le_one_iff PNat.le_one_iff

theorem lt_add_left (n m : ℕ+) : n < m + n :=
  lt_add_of_pos_left _ m.2
#align pnat.lt_add_left PNat.lt_add_left

-- Porting note: deprecated
section deprecated

set_option linter.deprecated false

@[simp, norm_cast, deprecated]
theorem coe_bit0 (a : ℕ+) : ((bit0 a : ℕ+) : ℕ) = bit0 (a : ℕ) :=
  rfl
#align pnat.coe_bit0 PNat.coe_bit0

@[simp, norm_cast, deprecated]
theorem coe_bit1 (a : ℕ+) : ((bit1 a : ℕ+) : ℕ) = bit1 (a : ℕ) :=
  rfl
#align pnat.coe_bit1 PNat.coe_bit1

end deprecated

-- Porting note:
-- mathlib3 statement was
-- `((m ^ n : ℕ+) : ℕ) = (m : ℕ) ^ n`
-- where the left `^ : ℕ+ → ℕ → ℕ+` was `monoid.has_pow`.
-- Atm writing `m ^ n` means automatically `(↑m) ^ n`.
@[simp, norm_cast]
theorem pow_coe (m : ℕ+) (n : ℕ) : ((Pow.pow m n : ℕ+) : ℕ) = (m : ℕ) ^ n :=
  rfl
#align pnat.pow_coe PNat.pow_coe

/-- Subtraction a - b is defined in the obvious way when
  a > b, and by a - b = 1 if a ≤ b.
-/
instance : Sub ℕ+ :=
  ⟨fun a b => toPNat' (a - b : ℕ)⟩

theorem sub_coe (a b : ℕ+) : ((a - b : ℕ+) : ℕ) = ite (b < a) (a - b : ℕ) 1 := by
  change (toPNat' _ : ℕ) = ite _ _ _
  split_ifs with h
  · exact toPNat'_coe (tsub_pos_of_lt h)
  · rw [tsub_eq_zero_iff_le.mpr (le_of_not_gt h : (a : ℕ) ≤ b)]
    rfl
#align pnat.sub_coe PNat.sub_coe

theorem add_sub_of_lt {a b : ℕ+} : a < b → a + (b - a) = b :=
  fun h =>
    PNat.eq <| by
      rw [add_coe, sub_coe, if_pos h]
      exact add_tsub_cancel_of_le h.le
#align pnat.add_sub_of_lt PNat.add_sub_of_lt

/-- If `n : ℕ+` is different from `1`, then it is the successor of some `k : ℕ+`. -/
theorem exists_eq_succ_of_ne_one : ∀ {n : ℕ+} (_ : n ≠ 1), ∃ k : ℕ+, n = k + 1
  | ⟨1, _⟩, h₁ => False.elim <| h₁ rfl
  | ⟨n + 2, _⟩, _ => ⟨⟨n + 1, by simp⟩, rfl⟩
#align pnat.exists_eq_succ_of_ne_one PNat.exists_eq_succ_of_ne_one

/-- Strong induction on `ℕ+`, with `n = 1` treated separately. -/
def caseStrongInductionOn {p : ℕ+ → Sort _} (a : ℕ+) (hz : p 1)
    (hi : ∀ n, (∀ m, m ≤ n → p m) → p (n + 1)) : p a := by
  apply strongInductionOn a
  rintro ⟨k, kprop⟩ hk
  cases' k with k
  · exact (lt_irrefl 0 kprop).elim
  cases' k with k
  · exact hz
  exact hi ⟨k.succ, Nat.succ_pos _⟩ fun m hm => hk _ (lt_succ_iff.2 hm)
#align pnat.case_strong_induction_on PNat.caseStrongInductionOn

/-- An induction principle for `ℕ+`: it takes values in `Sort*`, so it applies also to Types,
not only to `Prop`. -/
@[elab_as_elim]
def recOn (n : ℕ+) {p : ℕ+ → Sort _} (p1 : p 1) (hp : ∀ n, p n → p (n + 1)) : p n := by
  rcases n with ⟨n, h⟩
  induction' n with n IH
  · exact absurd h (by decide)
  · cases' n with n
    · exact p1
    · exact hp _ (IH n.succ_pos)
#align pnat.rec_on PNat.recOn

@[simp]
theorem recOn_one {p} (p1 hp) : @PNat.recOn 1 p p1 hp = p1 :=
  rfl
#align pnat.rec_on_one PNat.recOn_one

@[simp]
theorem recOn_succ (n : ℕ+) {p : ℕ+ → Sort _} (p1 hp) :
    @PNat.recOn (n + 1) p p1 hp = hp n (@PNat.recOn n p p1 hp) := by
  cases' n with n h
  cases n <;> [exact absurd h (by decide), rfl]
#align pnat.rec_on_succ PNat.recOn_succ

theorem modDivAux_spec :
    ∀ (k : ℕ+) (r q : ℕ) (_ : ¬(r = 0 ∧ q = 0)),
      ((modDivAux k r q).1 : ℕ) + k * (modDivAux k r q).2 = r + k * q
  | k, 0, 0, h => (h ⟨rfl, rfl⟩).elim
  | k, 0, q + 1, _ => by
    change (k : ℕ) + (k : ℕ) * (q + 1).pred = 0 + (k : ℕ) * (q + 1)
    rw [Nat.pred_succ, Nat.mul_succ, zero_add, add_comm]
  | k, r + 1, q, _ => rfl
#align pnat.mod_div_aux_spec PNat.modDivAux_spec

theorem mod_add_div (m k : ℕ+) : (mod m k + k * div m k : ℕ) = m := by
  let h₀ := Nat.mod_add_div (m : ℕ) (k : ℕ)
  have : ¬((m : ℕ) % (k : ℕ) = 0 ∧ (m : ℕ) / (k : ℕ) = 0) := by
    rintro ⟨hr, hq⟩
    rw [hr, hq, mul_zero, zero_add] at h₀
    exact (m.ne_zero h₀.symm).elim
  have := modDivAux_spec k ((m : ℕ) % (k : ℕ)) ((m : ℕ) / (k : ℕ)) this
  exact this.trans h₀
#align pnat.mod_add_div PNat.mod_add_div

theorem div_add_mod (m k : ℕ+) : (k * div m k + mod m k : ℕ) = m :=
  (add_comm _ _).trans (mod_add_div _ _)
#align pnat.div_add_mod PNat.div_add_mod

theorem mod_add_div' (m k : ℕ+) : (mod m k + div m k * k : ℕ) = m := by
  rw [mul_comm]
  exact mod_add_div _ _
#align pnat.mod_add_div' PNat.mod_add_div'

theorem div_add_mod' (m k : ℕ+) : (div m k * k + mod m k : ℕ) = m := by
  rw [mul_comm]
  exact div_add_mod _ _
#align pnat.div_add_mod' PNat.div_add_mod'

theorem mod_le (m k : ℕ+) : mod m k ≤ m ∧ mod m k ≤ k := by
  change (mod m k : ℕ) ≤ (m : ℕ) ∧ (mod m k : ℕ) ≤ (k : ℕ)
  rw [mod_coe]
  split_ifs with h
  · have hm : (m : ℕ) > 0 := m.pos
    rw [← Nat.mod_add_div (m : ℕ) (k : ℕ), h, zero_add] at hm ⊢
    by_cases h₁ : (m : ℕ) / (k : ℕ) = 0
    · rw [h₁, mul_zero] at hm
      exact (lt_irrefl _ hm).elim
    · let h₂ : (k : ℕ) * 1 ≤ k * (m / k) :=
        -- Porting note : Specified type of `h₂` explicitely because `rw` could not unify
        -- `succ 0` with `1`.
        Nat.mul_le_mul_left (k : ℕ) (Nat.succ_le_of_lt (Nat.pos_of_ne_zero h₁))
      rw [mul_one] at h₂
      exact ⟨h₂, le_refl (k : ℕ)⟩
  · exact ⟨Nat.mod_le (m : ℕ) (k : ℕ), (Nat.mod_lt (m : ℕ) k.pos).le⟩

#align pnat.mod_le PNat.mod_le

theorem dvd_iff {k m : ℕ+} : k ∣ m ↔ (k : ℕ) ∣ (m : ℕ) := by
  constructor <;> intro h
  · rcases h with ⟨_, rfl⟩
    apply dvd_mul_right
  · rcases h with ⟨a, h⟩
    cases a with
    | zero =>
      contrapose h
      apply ne_zero
    | succ n =>
      use ⟨n.succ, n.succ_pos⟩
      rw [← coe_inj, h, mul_coe, mk_coe]
#align pnat.dvd_iff PNat.dvd_iff

theorem dvd_iff' {k m : ℕ+} : k ∣ m ↔ mod m k = k := by
  rw [dvd_iff]
  rw [Nat.dvd_iff_mod_eq_zero]; constructor
  · intro h
    apply PNat.eq
    rw [mod_coe, if_pos h]
  · intro h
    by_cases h' : (m : ℕ) % (k : ℕ) = 0
    · exact h'
    · replace h : (mod m k : ℕ) = (k : ℕ) := congr_arg _ h
      rw [mod_coe, if_neg h'] at h
      exact ((Nat.mod_lt (m : ℕ) k.pos).ne h).elim
#align pnat.dvd_iff' PNat.dvd_iff'

theorem le_of_dvd {m n : ℕ+} : m ∣ n → m ≤ n := by
  rw [dvd_iff']
  intro h
  rw [← h]
  apply (mod_le n m).left
#align pnat.le_of_dvd PNat.le_of_dvd

theorem mul_div_exact {m k : ℕ+} (h : k ∣ m) : k * divExact m k = m := by
  apply PNat.eq; rw [mul_coe]
  change (k : ℕ) * (div m k).succ = m
  rw [← div_add_mod m k, dvd_iff'.mp h, Nat.mul_succ]
#align pnat.mul_div_exact PNat.mul_div_exact

theorem dvd_antisymm {m n : ℕ+} : m ∣ n → n ∣ m → m = n := fun hmn hnm =>
  (le_of_dvd hmn).antisymm (le_of_dvd hnm)
#align pnat.dvd_antisymm PNat.dvd_antisymm

theorem dvd_one_iff (n : ℕ+) : n ∣ 1 ↔ n = 1 :=
  ⟨fun h => dvd_antisymm h (one_dvd n), fun h => h.symm ▸ dvd_refl 1⟩
#align pnat.dvd_one_iff PNat.dvd_one_iff

theorem pos_of_div_pos {n : ℕ+} {a : ℕ} (h : a ∣ n) : 0 < a := by
  apply pos_iff_ne_zero.2
  intro hzero
  rw [hzero] at h
  exact PNat.ne_zero n (eq_zero_of_zero_dvd h)
#align pnat.pos_of_div_pos PNat.pos_of_div_pos

end PNat