/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.linear_algebra.basic
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Projection to a subspace

In this file we define
* `linear_proj_of_is_compl (p q : submodule R E) (h : is_compl p q)`: the projection of a module `E`
  to a submodule `p` along its complement `q`; it is the unique linear map `f : E → p` such that
  `f x = x` for `x ∈ p` and `f x = 0` for `x ∈ q`.
* `is_compl_equiv_proj p`: equivalence between submodules `q` such that `is_compl p q` and
  projections `f : E → p`, `∀ x ∈ p, f x = x`.

We also provide some lemmas justifying correctness of our definitions.

## Tags

projection, complement subspace
-/

namespace linear_map

theorem ker_id_sub_eq_of_proj {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {f : linear_map R E ↥p} (hf : ∀ (x : ↥p), coe_fn f ↑x = x) : ker (id - comp (submodule.subtype p) f) = p := sorry

theorem range_eq_of_proj {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {f : linear_map R E ↥p} (hf : ∀ (x : ↥p), coe_fn f ↑x = x) : range f = ⊤ :=
  iff.mpr range_eq_top fun (x : ↥p) => Exists.intro (↑x) (hf x)

theorem is_compl_of_proj {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {f : linear_map R E ↥p} (hf : ∀ (x : ↥p), coe_fn f ↑x = x) : is_compl p (ker f) := sorry

end linear_map

namespace submodule

/-- If `q` is a complement of `p`, then `M/p ≃ q`. -/
def quotient_equiv_of_is_compl {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) : linear_equiv R (quotient p) ↥q :=
  linear_equiv.symm (linear_equiv.of_bijective (linear_map.comp (mkq p) (submodule.subtype q)) sorry sorry)

@[simp] theorem quotient_equiv_of_is_compl_symm_apply {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) (x : ↥q) : coe_fn (linear_equiv.symm (quotient_equiv_of_is_compl p q h)) x = quotient.mk ↑x :=
  rfl

@[simp] theorem quotient_equiv_of_is_compl_apply_mk_coe {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) (x : ↥q) : coe_fn (quotient_equiv_of_is_compl p q h) (quotient.mk ↑x) = x :=
  linear_equiv.apply_symm_apply (quotient_equiv_of_is_compl p q h) x

@[simp] theorem mk_quotient_equiv_of_is_compl_apply {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) (x : quotient p) : quotient.mk ↑(coe_fn (quotient_equiv_of_is_compl p q h) x) = x :=
  linear_equiv.symm_apply_apply (quotient_equiv_of_is_compl p q h) x

/-- If `q` is a complement of `p`, then `p × q` is isomorphic to `E`. It is the unique
linear map `f : E → p` such that `f x = x` for `x ∈ p` and `f x = 0` for `x ∈ q`. -/
def prod_equiv_of_is_compl {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) : linear_equiv R (↥p × ↥q) E :=
  linear_equiv.of_bijective (linear_map.coprod (submodule.subtype p) (submodule.subtype q)) sorry sorry

@[simp] theorem coe_prod_equiv_of_is_compl {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) : ↑(prod_equiv_of_is_compl p q h) = linear_map.coprod (submodule.subtype p) (submodule.subtype q) :=
  rfl

@[simp] theorem coe_prod_equiv_of_is_compl' {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) (x : ↥p × ↥q) : coe_fn (prod_equiv_of_is_compl p q h) x = ↑(prod.fst x) + ↑(prod.snd x) :=
  rfl

@[simp] theorem prod_equiv_of_is_compl_symm_apply_left {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) (x : ↥p) : coe_fn (linear_equiv.symm (prod_equiv_of_is_compl p q h)) ↑x = (x, 0) := sorry

@[simp] theorem prod_equiv_of_is_compl_symm_apply_right {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) (x : ↥q) : coe_fn (linear_equiv.symm (prod_equiv_of_is_compl p q h)) ↑x = (0, x) := sorry

@[simp] theorem prod_equiv_of_is_compl_symm_apply_fst_eq_zero {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) {x : E} : prod.fst (coe_fn (linear_equiv.symm (prod_equiv_of_is_compl p q h)) x) = 0 ↔ x ∈ q := sorry

@[simp] theorem prod_equiv_of_is_compl_symm_apply_snd_eq_zero {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) {x : E} : prod.snd (coe_fn (linear_equiv.symm (prod_equiv_of_is_compl p q h)) x) = 0 ↔ x ∈ p := sorry

/-- Projection to a submodule along its complement. -/
def linear_proj_of_is_compl {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : submodule R E) (h : is_compl p q) : linear_map R E ↥p :=
  linear_map.comp (linear_map.fst R ↥p ↥q) ↑(linear_equiv.symm (prod_equiv_of_is_compl p q h))

@[simp] theorem linear_proj_of_is_compl_apply_left {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) (x : ↥p) : coe_fn (linear_proj_of_is_compl p q h) ↑x = x := sorry

@[simp] theorem linear_proj_of_is_compl_range {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) : linear_map.range (linear_proj_of_is_compl p q h) = ⊤ :=
  linear_map.range_eq_of_proj (linear_proj_of_is_compl_apply_left h)

@[simp] theorem linear_proj_of_is_compl_apply_eq_zero_iff {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) {x : E} : coe_fn (linear_proj_of_is_compl p q h) x = 0 ↔ x ∈ q := sorry

theorem linear_proj_of_is_compl_apply_right' {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) (x : E) (hx : x ∈ q) : coe_fn (linear_proj_of_is_compl p q h) x = 0 :=
  iff.mpr (linear_proj_of_is_compl_apply_eq_zero_iff h) hx

@[simp] theorem linear_proj_of_is_compl_apply_right {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) (x : ↥q) : coe_fn (linear_proj_of_is_compl p q h) ↑x = 0 :=
  linear_proj_of_is_compl_apply_right' h (↑x) (subtype.property x)

@[simp] theorem linear_proj_of_is_compl_ker {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) : linear_map.ker (linear_proj_of_is_compl p q h) = q :=
  ext fun (x : E) => iff.trans linear_map.mem_ker (linear_proj_of_is_compl_apply_eq_zero_iff h)

theorem linear_proj_of_is_compl_comp_subtype {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) : linear_map.comp (linear_proj_of_is_compl p q h) (submodule.subtype p) = linear_map.id :=
  linear_map.ext (linear_proj_of_is_compl_apply_left h)

theorem linear_proj_of_is_compl_idempotent {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} {q : submodule R E} (h : is_compl p q) (x : E) : coe_fn (linear_proj_of_is_compl p q h) ↑(coe_fn (linear_proj_of_is_compl p q h) x) =
  coe_fn (linear_proj_of_is_compl p q h) x :=
  linear_proj_of_is_compl_apply_left h (coe_fn (linear_proj_of_is_compl p q h) x)

end submodule

namespace linear_map

@[simp] theorem linear_proj_of_is_compl_of_proj {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {p : submodule R E} (f : linear_map R E ↥p) (hf : ∀ (x : ↥p), coe_fn f ↑x = x) : submodule.linear_proj_of_is_compl p (ker f) (is_compl_of_proj hf) = f := sorry

/-- If `f : E →ₗ[R] F` and `g : E →ₗ[R] G` are two surjective linear maps and
their kernels are complement of each other, then `x ↦ (f x, g x)` defines
a linear equivalence `E ≃ₗ[R] F × G`. -/
def equiv_prod_of_surjective_of_is_compl {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {F : Type u_3} [add_comm_group F] [module R F] {G : Type u_4} [add_comm_group G] [module R G] (f : linear_map R E F) (g : linear_map R E G) (hf : range f = ⊤) (hg : range g = ⊤) (hfg : is_compl (ker f) (ker g)) : linear_equiv R E (F × G) :=
  linear_equiv.of_bijective (prod f g) sorry sorry

@[simp] theorem coe_equiv_prod_of_surjective_of_is_compl {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {F : Type u_3} [add_comm_group F] [module R F] {G : Type u_4} [add_comm_group G] [module R G] {f : linear_map R E F} {g : linear_map R E G} (hf : range f = ⊤) (hg : range g = ⊤) (hfg : is_compl (ker f) (ker g)) : ↑(equiv_prod_of_surjective_of_is_compl f g hf hg hfg) = prod f g :=
  rfl

@[simp] theorem equiv_prod_of_surjective_of_is_compl_apply {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] {F : Type u_3} [add_comm_group F] [module R F] {G : Type u_4} [add_comm_group G] [module R G] {f : linear_map R E F} {g : linear_map R E G} (hf : range f = ⊤) (hg : range g = ⊤) (hfg : is_compl (ker f) (ker g)) (x : E) : coe_fn (equiv_prod_of_surjective_of_is_compl f g hf hg hfg) x = (coe_fn f x, coe_fn g x) :=
  rfl

end linear_map

namespace submodule

/-- Equivalence between submodules `q` such that `is_compl p q` and linear maps `f : E →ₗ[R] p`
such that `∀ x : p, f x = x`. -/
def is_compl_equiv_proj {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) : (Subtype fun (q : submodule R E) => is_compl p q) ≃ Subtype fun (f : linear_map R E ↥p) => ∀ (x : ↥p), coe_fn f ↑x = x :=
  equiv.mk
    (fun (q : Subtype fun (q : submodule R E) => is_compl p q) =>
      { val := linear_proj_of_is_compl p ↑q sorry, property := sorry })
    (fun (f : Subtype fun (f : linear_map R E ↥p) => ∀ (x : ↥p), coe_fn f ↑x = x) =>
      { val := linear_map.ker ↑f, property := sorry })
    sorry sorry

@[simp] theorem coe_is_compl_equiv_proj_apply {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (q : Subtype fun (q : submodule R E) => is_compl p q) : ↑(coe_fn (is_compl_equiv_proj p) q) = linear_proj_of_is_compl p (↑q) (subtype.property q) :=
  rfl

@[simp] theorem coe_is_compl_equiv_proj_symm_apply {R : Type u_1} [ring R] {E : Type u_2} [add_comm_group E] [module R E] (p : submodule R E) (f : Subtype fun (f : linear_map R E ↥p) => ∀ (x : ↥p), coe_fn f ↑x = x) : ↑(coe_fn (equiv.symm (is_compl_equiv_proj p)) f) = linear_map.ker ↑f :=
  rfl


namespace fol_01

variable A : Type
variable R : A → A → Prop

theorem fol_01 : (∀ x y, R x y) → (∀ x, R x x) :=
assume h1: (∀ x y, R x y),
show ∀ x, R x x, from
  (assume hx: A,
   h1 hx hx)

end fol_01


/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.preserves.limits
import Mathlib.category_theory.limits.shapes.default
import Mathlib.PostPort

universes u₁ u₂ v

namespace Mathlib

/-!
# Preserving terminal object

Constructions to relate the notions of preserving terminal objects and reflecting terminal objects
to concrete objects.

In particular, we show that `terminal_comparison G` is an isomorphism iff `G` preserves terminal
objects.
-/

namespace category_theory.limits

/--
The map of an empty cone is a limit iff the mapped object is terminal.
-/
def is_limit_map_cone_empty_cone_equiv {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) (X : C) :
    is_limit (functor.map_cone G (as_empty_cone X)) ≃ is_terminal (functor.obj G X) :=
  equiv.trans
    (equiv.symm
      (is_limit.postcompose_hom_equiv (functor.empty_ext (functor.empty C ⋙ G) (functor.empty D))
        (functor.map_cone G (as_empty_cone X))))
    (is_limit.equiv_iso_limit
      (cones.ext
        (iso.refl
          (cone.X
            (functor.obj
              (cones.postcompose
                (iso.hom (functor.empty_ext (functor.empty C ⋙ G) (functor.empty D))))
              (functor.map_cone G (as_empty_cone X)))))
        sorry))

/-- The property of preserving terminal objects expressed in terms of `is_terminal`. -/
def is_terminal_obj_of_is_terminal {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D)
    (X : C) [preserves_limit (functor.empty C) G] (l : is_terminal X) :
    is_terminal (functor.obj G X) :=
  coe_fn (is_limit_map_cone_empty_cone_equiv G X) (preserves_limit.preserves l)

/-- The property of reflecting terminal objects expressed in terms of `is_terminal`. -/
def is_terminal_of_is_terminal_obj {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D)
    (X : C) [reflects_limit (functor.empty C) G] (l : is_terminal (functor.obj G X)) :
    is_terminal X :=
  reflects_limit.reflects (coe_fn (equiv.symm (is_limit_map_cone_empty_cone_equiv G X)) l)

/--
If `G` preserves the terminal object and `C` has a terminal object, then the image of the terminal
object is terminal.
-/
def is_limit_of_has_terminal_of_preserves_limit {C : Type u₁} [category C] {D : Type u₂}
    [category D] (G : C ⥤ D) [has_terminal C] [preserves_limit (functor.empty C) G] :
    is_terminal (functor.obj G (⊤_C)) :=
  is_terminal_obj_of_is_terminal G (⊤_C) terminal_is_terminal

/--
If `C` has a terminal object and `G` preserves terminal objects, then `D` has a terminal object
also.
Note this property is somewhat unique to (co)limits of the empty diagram: for general `J`, if `C`
has limits of shape `J` and `G` preserves them, then `D` does not necessarily have limits of shape
`J`.
-/
theorem has_terminal_of_has_terminal_of_preserves_limit {C : Type u₁} [category C] {D : Type u₂}
    [category D] (G : C ⥤ D) [has_terminal C] [preserves_limit (functor.empty C) G] :
    has_terminal D :=
  has_limits_of_shape.mk
    fun (F : discrete pempty ⥤ D) => has_limit_of_iso (iso.symm (functor.unique_from_empty F))

/--
If the terminal comparison map for `G` is an isomorphism, then `G` preserves terminal objects.
-/
def preserves_terminal.of_iso_comparison {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) [has_terminal C] [has_terminal D] [i : is_iso (terminal_comparison G)] :
    preserves_limit (functor.empty C) G :=
  preserves_limit_of_preserves_limit_cone terminal_is_terminal
    (coe_fn (equiv.symm (is_limit_map_cone_empty_cone_equiv G (⊤_C)))
      (is_limit.of_point_iso (limit.is_limit (functor.empty D))))

/-- If there is any isomorphism `G.obj ⊤ ⟶ ⊤`, then `G` preserves terminal objects. -/
def preserves_terminal_of_is_iso {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D)
    [has_terminal C] [has_terminal D] (f : functor.obj G (⊤_C) ⟶ ⊤_D) [i : is_iso f] :
    preserves_limit (functor.empty C) G :=
  preserves_terminal.of_iso_comparison G

/-- If there is any isomorphism `G.obj ⊤ ≅ ⊤`, then `G` preserves terminal objects. -/
def preserves_terminal_of_iso {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D)
    [has_terminal C] [has_terminal D] (f : functor.obj G (⊤_C) ≅ ⊤_D) :
    preserves_limit (functor.empty C) G :=
  preserves_terminal_of_is_iso G (iso.hom f)

/-- If `G` preserves terminal objects, then the terminal comparison map for `G` an isomorphism. -/
def preserves_terminal.iso {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D)
    [has_terminal C] [has_terminal D] [preserves_limit (functor.empty C) G] :
    functor.obj G (⊤_C) ≅ ⊤_D :=
  is_limit.cone_point_unique_up_to_iso (is_limit_of_has_terminal_of_preserves_limit G)
    (limit.is_limit (functor.empty D))

@[simp] theorem preserves_terminal.iso_hom {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) [has_terminal C] [has_terminal D] [preserves_limit (functor.empty C) G] :
    iso.hom (preserves_terminal.iso G) = terminal_comparison G :=
  rfl

protected instance terminal_comparison.category_theory.is_iso {C : Type u₁} [category C]
    {D : Type u₂} [category D] (G : C ⥤ D) [has_terminal C] [has_terminal D]
    [preserves_limit (functor.empty C) G] : is_iso (terminal_comparison G) :=
  eq.mpr sorry (is_iso.of_iso (preserves_terminal.iso G))

end Mathlib


import algebra.group.defs
import data.set.basic
import data.set.finite
import data.set.prod
import data.set.pointwise
import algebra.support
import order.complete_lattice
import order.filter.basic
import order.filter.pointwise

open filter function
open_locale filter pointwise

variables {α β G M : Type*}

/-!
### Partial scalar actions
-/

/-- Typeclass for partial scalar actions. `smul_defined a x` means that `a • x` is defined. -/
class has_partial_smul (M α : Type*) extends has_smul M α :=
(smul_defined : M → α → Prop)

export has_partial_smul

/-- The domain of defintion of a partial action. -/
def smul_dom (M α : Type*) [has_partial_smul M α] := { p : M × α | smul_defined p.1 p.2 }

lemma smul_dom_mem_iff [has_partial_smul M α] {a : M} {x : α} :
  smul_defined a x ↔ (a, x) ∈ smul_dom M α := by tautology

/-- The partial_smul of two sets. -/
def set.partial_smul [has_partial_smul M α] (t : set M) (s : set α) : set α :=
  (λ p : M × α, p.1 • p.2) '' ((t ×ˢ s) ∩ smul_dom M α)

/-- The partial scalar multiplication of two filters. -/
def filter.partial_smul [has_partial_smul M α]
  (g : filter M) (f : filter α) : filter α :=
map (λ p : M × α, p.1 • p.2) ((g ×ᶠ f) ⊓ 𝓟 (smul_dom M α))

/-- Typeclass for partial actions of groups. -/
class partial_mul_action (G α : Type*) [group G]
  extends has_partial_smul G α :=
(one_smul : ∀ (x : α), smul_defined 1 x ∧ (1 : G) • x = x)
(mul_smul : ∀ {a b : G} {x : α}, smul_defined b x → smul_defined a (b • x) →
  smul_defined (a * b) x ∧ (a * b) • x = a • (b • x))
(inv_smul_cancel_left : ∀ {a : G} {x y : α},
  smul_defined a x → a • x = y → smul_defined a⁻¹ y ∧ x = a⁻¹ • y)

export partial_mul_action

lemma partial_mul_action.injective [group G] [partial_mul_action G α]:
  ∀ {a : G} {x y : α}, smul_defined a x → smul_defined a y → a • x = a • y → x = y :=
begin
  intros a x y hdef₁ hdef₂ heq₁,
  obtain ⟨hdef₃, heq₂⟩ := inv_smul_cancel_left hdef₁ heq₁,
  obtain ⟨hdef₄, heq₃⟩ := mul_smul hdef₂ hdef₃,
  simp [(one_smul y).2] at heq₃,
  exact (rfl.congr (eq.symm heq₃)).mp heq₂,
end

/-!
### Enveloping action
-/

def envelope (G α : Type*) [group G] [partial_mul_action G α] : G × α → G × α → Prop :=
 λ ⟨a, x⟩ ⟨b, y⟩, smul_defined (b⁻¹ * a) x ∧ (b⁻¹ * a) • x = y

namespace envelope

abbreviation space (G α : Type*) [group G] [partial_mul_action G α] := quot (envelope G α)

def embed (G : Type*) {α : Type*} [group G] [partial_mul_action G α]
  (x : α) : space G α := quot.mk (envelope G α) (1, x)

variables [group G] [partial_mul_action G α]

lemma is_reflexive : reflexive (envelope G α) :=
begin
  rintro (⟨a, x⟩ : G × α),
  unfold envelope, simp,
  exact one_smul x,
end

lemma is_symmetric : symmetric (envelope G α) :=
begin
  rintro ⟨a, x⟩ ⟨b, y⟩,
  unfold envelope, rintro ⟨hdef, heq⟩,
  obtain ⟨hdef', heq'⟩ := inv_smul_cancel_left hdef heq,
  simp at *, exact ⟨hdef', eq.symm heq'⟩,
end

lemma is_transitive : transitive (envelope G α) :=
begin
  rintro ⟨a, x⟩ ⟨b, y⟩ ⟨c, z⟩ : G × α,
  unfold envelope,
  rintro ⟨hdef₁, heq₁⟩ ⟨hdef₂, heq₂⟩,
  rw [← heq₁] at hdef₂,
  rw [← heq₁] at heq₂,
  obtain ⟨hdef₃, heq₃⟩ := mul_smul hdef₁ hdef₂,
  simp [mul_assoc] at hdef₃,
  simp [mul_assoc, heq₂] at heq₃,
  exact ⟨hdef₃, heq₃⟩,
end

lemma is_equivalence : equivalence (envelope G α) := ⟨is_reflexive, is_symmetric, is_transitive⟩

instance : setoid (G × α) :=
{ r := envelope G α,
  iseqv := is_equivalence }

instance : has_smul G (G × α) := ⟨λ a ⟨b, x⟩, (a * b, x)⟩

lemma act_congr (a : G) (bx cy : G × α) (heq : bx ≈ cy) : a • bx ≈ a • cy :=
begin
  obtain ⟨b, x⟩ := bx,
  obtain ⟨c, y⟩ := cy,
  change smul_defined ((a * c)⁻¹ * (a * b)) x ∧ ((a * c)⁻¹ * (a * b)) • x = y,
  simp [mul_assoc],
  assumption,
end

lemma act_congr_sound (a : G) (bx cy : G × α) (heq : bx ≈ cy) :
  ⟦a • bx⟧ = ⟦a • cy⟧ :=
quotient.sound (act_congr a bx cy heq)

def act_lifted (a : G) (bx : G × α) : space G α := ⟦a • bx⟧

instance : has_smul G (space G α) :=
⟨λ a bx, quotient.lift (act_lifted a) (act_congr_sound a) bx⟩

end envelope

/-!
### Extra set stuff
-/

namespace set

lemma subset_eq_nonempty {s t : set α} (hsub : t ⊆ s) (hne : t.nonempty) : s.nonempty :=
begin
  rw ← empty_ssubset at *,
  exact ssubset_of_ssubset_of_subset hne hsub,
end

lemma ne_empty_iff_exists_elem {s : set α} : ¬ (s = ∅) ↔ ∃ x, x ∈ s :=
begin
  split,
  { intros hne,
    change s ≠ ∅ at hne,
    rw [ne_empty_iff_nonempty, nonempty_def] at hne,
    assumption },
  { intros hexists,
    change s ≠ ∅,
    rw ne_empty_iff_nonempty,
    exact nonempty_def.mpr hexists }
end

end set

/-!
### Extra lattice stuff
-/

lemma Sup_image_le [complete_semilattice_Sup β]
  {f g : α → β} (hle : f ≤ g) (s : set α) : Sup (f '' s) ≤ Sup (g '' s) :=
begin
  refine Sup_le_Sup_of_forall_exists_le _,
  intros y hy,
  obtain ⟨x, hx, heq⟩ := set.mem_image_iff_bex.mp hy,
  use g x, refine ⟨set.mem_image_of_mem g hx, _⟩,
  rw ← heq, exact hle x,
end

/-!
### Extra filter stuff
-/

namespace filter

lemma inf_neq_bot_iff {f g : filter α} :
  (f ⊓ g) ≠ ⊥ ↔ ∀ (s ∈ f) (t ∈ g), s ∩ t ≠ ∅ :=
begin
  let hiff := iff.not inf_eq_bot_iff,
  push_neg at hiff,
  assumption,
end

lemma prod_inf_principal_mem_iff {f : filter α} {g : filter β} {s : set (α × β)} :
  ∀ t, t ∈ (f ×ᶠ g) ⊓ 𝓟 s ↔ ∃ (u ∈ f) (v ∈ g), (u ×ˢ v) ∩ s ⊆ t :=
begin
  intros t,
  split,
  { intro hmem,
    obtain ⟨l, hl, r, hr, heq⟩ := mem_inf_iff.mp hmem,
    obtain ⟨u, hu, v, hv, hsub⟩ := mem_prod_iff.mp hl,
    let hsub' := mem_principal.mp hr,
    let hsub'' := set.inter_subset_inter hsub hsub',
    rw ← heq at hsub'',
    exact ⟨u, hu, v, hv, hsub''⟩,
  },
  { rintro ⟨u, hu, v, hv, hsub⟩,
    rw mem_inf_iff_superset,
    have hsub' : u ×ˢ v ∈ f ×ᶠ g :=
      mem_prod_iff.mpr ⟨u, hu, v, hv, subset_refl (u ×ˢ v) ⟩,
    exact ⟨u ×ˢ v, hsub', s, mem_principal_self s, hsub⟩,
  },
end

lemma inf_ne_bot {f g : filter α} [f.ne_bot] (hle : f ≤ g) : (f ⊓ g).ne_bot :=
begin
  rw inf_of_le_left hle,
  assumption
end

/-!
### Extra filter pointwise stuff
-/

lemma mem_inv_iff [_root_.has_involutive_inv α] {f : filter α} {s : set α} :
  s ∈ f⁻¹ ↔ ∃ t ∈ f, t⁻¹ ⊆ s :=
begin
  split,
  { assume hmem,
    change s ∈ map has_inv.inv f at hmem,
    rw mem_map_iff_exists_image at hmem,
    obtain ⟨t, ht, hsub⟩ := hmem,
    rw [set.image_inv] at hsub,
    exact ⟨t, ht, hsub⟩ },
  { rintro ⟨t, ht, hsub⟩,
    exact mem_of_superset (filter.inv_mem_inv ht) hsub }
end

lemma mem_inv_smul_iff [has_smul G α] [_root_.has_involutive_inv G] {g : filter G} {f : filter α} {u : set α} :
  u ∈ g⁻¹ • f ↔ ∃ t s, t ∈ g ∧ s ∈ f ∧ t⁻¹ • s ⊆ u :=
begin
  split,
  { intros hmem,
    obtain ⟨t', s, ht', hs, hsub⟩ := mem_smul.mp hmem,
    rw mem_inv_iff at ht',
    obtain ⟨t, ht, hsub'⟩ := ht',
    refine ⟨t, s, ht, hs, _⟩,
    exact subset_trans (set.smul_subset_smul_right hsub') hsub },
  { rintros ⟨t, s, ht, hs, hsub⟩,
    have ht' : t⁻¹ ∈ g⁻¹ := inv_mem_inv ht,
    rw filter.mem_smul,
    refine ⟨t⁻¹, s, ht', hs, hsub⟩ }
end

lemma inv_smul_inf_ne_bot [group G] [mul_action G α] {g : filter G} {f f' : filter α}
  (hle : f' ≤ g • f) [hf' : f'.ne_bot] : ((g⁻¹ • f') ⊓ f).ne_bot :=
begin
  rw ← forall_mem_nonempty_iff_ne_bot,
  intros s hmem,
  obtain ⟨s₁, hs₁, s₂, hs₂, hsub₁⟩ := mem_inf_iff_superset.mp hmem,
  obtain ⟨t₁, s₃, ht₁, hs₃, hsub₂⟩ := mem_inv_smul_iff.mp hs₁,
  refine set.subset_eq_nonempty hsub₁ _,
  refine set.subset_eq_nonempty (set.inter_subset_inter_left s₂ hsub₂) _,
  let s₄ : set α := s₃ ∩ (t₁ • s₂),
  have hne : s₄.nonempty,
    from forall_mem_nonempty_iff_ne_bot.mpr hf' s₄
      (f'.inter_sets hs₃ (filter.le_def.mp hle (t₁ • s₂) (filter.smul_mem_smul ht₁ hs₂))),
  obtain ⟨y, hy⟩ := set.nonempty_def.mp hne,
  rw set.nonempty_def,
  obtain ⟨hy₁, hy₂⟩ := hy,
  change y ∈ set.image2 (•) t₁ s₂ at hy₂,
  obtain ⟨a, x, ha, hx, hy'⟩ := set.mem_image2.mp hy₂,
  have heq' : a⁻¹ • y = x, by simp [← hy', ← mul_action.mul_smul],
  have hmem' : x ∈ t₁⁻¹ • s₃,
  begin
    simp [← heq'],
    have : a⁻¹ ∈ has_inv.inv '' t₁, from set.mem_image_of_mem (has_inv.inv) ha,
    have : a⁻¹ ∈ t₁⁻¹, by rwa set.image_inv at this,
    exact set.mem_image2_of_mem this hy₁,
  end,
  exact ⟨x, hmem', hx⟩,
end

-- lemma mem_partial_smul_of_le_iff [has_partial_smul M α] {g : filter M} {f : filter α} {h : ultrafilter α}
--   (hle : ↑h ≤ partial_smul g f) {s : set α} :
--   s ∈ h ↔ ∃ t s, t ∈ g ∧ s ∈ f ∧
end filter


/-
Copyright (c) 2018 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 analysis.normed_space.lp_space
import topology.sets.compacts

/-!
# The Kuratowski embedding

Any separable metric space can be embedded isometrically in `ℓ^∞(ℝ)`.
-/

noncomputable theory

open set metric topological_space
open_locale ennreal
local notation `ℓ_infty_ℝ`:= lp (λ n : ℕ, ℝ) ∞

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

namespace Kuratowski_embedding

/-! ### Any separable metric space can be embedded isometrically in ℓ^∞(ℝ) -/

variables {f g : ℓ_infty_ℝ} {n : ℕ} {C : ℝ} [metric_space α] (x : ℕ → α) (a b : α)

/-- A metric space can be embedded in `l^∞(ℝ)` via the distances to points in
a fixed countable set, if this set is dense. This map is given in `Kuratowski_embedding`,
without density assumptions. -/
def embedding_of_subset : ℓ_infty_ℝ :=
⟨ λ n, dist a (x n) - dist (x 0) (x n),
  begin
    apply mem_ℓp_infty,
    use dist a (x 0),
    rintros - ⟨n, rfl⟩,
    exact abs_dist_sub_le _ _ _
  end ⟩

lemma embedding_of_subset_coe : embedding_of_subset x a n = dist a (x n) - dist (x 0) (x n) := rfl

/-- The embedding map is always a semi-contraction. -/
lemma embedding_of_subset_dist_le (a b : α) :
  dist (embedding_of_subset x a) (embedding_of_subset x b) ≤ dist a b :=
begin
  refine lp.norm_le_of_forall_le dist_nonneg (λn, _),
  simp only [lp.coe_fn_sub, pi.sub_apply, embedding_of_subset_coe, real.dist_eq],
  convert abs_dist_sub_le a b (x n) using 2,
  ring
end

/-- When the reference set is dense, the embedding map is an isometry on its image. -/
lemma embedding_of_subset_isometry (H : dense_range x) : isometry (embedding_of_subset x) :=
begin
  refine isometry.of_dist_eq (λa b, _),
  refine (embedding_of_subset_dist_le x a b).antisymm (le_of_forall_pos_le_add (λe epos, _)),
  /- First step: find n with dist a (x n) < e -/
  rcases metric.mem_closure_range_iff.1 (H a) (e/2) (half_pos epos) with ⟨n, hn⟩,
  /- Second step: use the norm control at index n to conclude -/
  have C : dist b (x n) - dist a (x n) = embedding_of_subset x b n - embedding_of_subset x a n :=
    by { simp only [embedding_of_subset_coe, sub_sub_sub_cancel_right] },
  have := calc
    dist a b ≤ dist a (x n) + dist (x n) b : dist_triangle _ _ _
    ...    = 2 * dist a (x n) + (dist b (x n) - dist a (x n)) : by { simp [dist_comm], ring }
    ...    ≤ 2 * dist a (x n) + |dist b (x n) - dist a (x n)| :
      by apply_rules [add_le_add_left, le_abs_self]
    ...    ≤ 2 * (e/2) + |embedding_of_subset x b n - embedding_of_subset x a n| :
      begin rw C, apply_rules [add_le_add, mul_le_mul_of_nonneg_left, hn.le, le_refl], norm_num end
    ...    ≤ 2 * (e/2) + dist (embedding_of_subset x b) (embedding_of_subset x a) :
    begin
      have : |embedding_of_subset x b n - embedding_of_subset x a n|
        ≤ dist (embedding_of_subset x b) (embedding_of_subset x a),
      { simpa [dist_eq_norm] using lp.norm_apply_le_norm ennreal.top_ne_zero
          (embedding_of_subset x b - embedding_of_subset x a) n },
      nlinarith,
    end
    ...    = dist (embedding_of_subset x b) (embedding_of_subset x a) + e : by ring,
  simpa [dist_comm] using this
end

/-- Every separable metric space embeds isometrically in `ℓ_infty_ℝ`. -/
theorem exists_isometric_embedding (α : Type u) [metric_space α] [separable_space α] :
  ∃(f : α → ℓ_infty_ℝ), isometry f :=
begin
  cases (univ : set α).eq_empty_or_nonempty with h h,
  { use (λ_, 0), assume x, exact absurd h (nonempty.ne_empty ⟨x, mem_univ x⟩) },
  { /- We construct a map x : ℕ → α with dense image -/
    rcases h with ⟨basepoint⟩,
    haveI : inhabited α := ⟨basepoint⟩,
    have : ∃s:set α, s.countable ∧ dense s := exists_countable_dense α,
    rcases this with ⟨S, ⟨S_countable, S_dense⟩⟩,
    rcases set.countable_iff_exists_subset_range.1 S_countable with ⟨x, x_range⟩,
    /- Use embedding_of_subset to construct the desired isometry -/
    exact ⟨embedding_of_subset x, embedding_of_subset_isometry x (S_dense.mono x_range)⟩ }
end
end Kuratowski_embedding

open topological_space Kuratowski_embedding

/-- The Kuratowski embedding is an isometric embedding of a separable metric space in `ℓ^∞(ℝ)`. -/
def Kuratowski_embedding (α : Type u) [metric_space α] [separable_space α] : α → ℓ_infty_ℝ :=
  classical.some (Kuratowski_embedding.exists_isometric_embedding α)

/-- The Kuratowski embedding is an isometry. -/
protected lemma Kuratowski_embedding.isometry (α : Type u) [metric_space α] [separable_space α] :
  isometry (Kuratowski_embedding α) :=
classical.some_spec (exists_isometric_embedding α)

/-- Version of the Kuratowski embedding for nonempty compacts -/
def nonempty_compacts.Kuratowski_embedding (α : Type u) [metric_space α] [compact_space α]
  [nonempty α] :
  nonempty_compacts ℓ_infty_ℝ :=
{ carrier := range (Kuratowski_embedding α),
  is_compact' := is_compact_range (Kuratowski_embedding.isometry α).continuous,
  nonempty' := range_nonempty _ }


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kevin Kappelmann
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.linarith.default
import Mathlib.tactic.abel
import Mathlib.algebra.ordered_group
import Mathlib.data.set.intervals.basic
import Mathlib.PostPort

universes u_2 l u_1

namespace Mathlib

/-!
# Floor and Ceil

## Summary

We define `floor`, `ceil`, and `nat_ceil` functions on linear ordered rings.

## Main Definitions

- `floor_ring` is a linear ordered ring with floor function.
- `floor x` is the greatest integer `z` such that `z ≤ x`.
- `fract x` is the fractional part of x, that is `x - floor x`.
- `ceil x` is the smallest integer `z` such that `x ≤ z`.
- `nat_ceil x` is the smallest nonnegative integer `n` with `x ≤ n`.

## Notations

- `⌊x⌋` is `floor x`.
- `⌈x⌉` is `ceil x`.

## Tags

rounding
-/

/--
A `floor_ring` is a linear ordered ring over `α` with a function
`floor : α → ℤ` satisfying `∀ (z : ℤ) (x : α), z ≤ floor x ↔ (z : α) ≤ x)`.
-/
class floor_ring (α : Type u_2) [linear_ordered_ring α]
where
  floor : α → ℤ
  le_floor : ∀ (z : ℤ) (x : α), z ≤ floor x ↔ ↑z ≤ x

protected instance int.floor_ring : floor_ring ℤ :=
  floor_ring.mk id sorry

/-- `floor x` is the greatest integer `z` such that `z ≤ x` -/
def floor {α : Type u_1} [linear_ordered_ring α] [floor_ring α] : α → ℤ :=
  floor_ring.floor

theorem le_floor {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {z : ℤ} {x : α} : z ≤ floor x ↔ ↑z ≤ x :=
  floor_ring.le_floor

theorem floor_lt {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {x : α} {z : ℤ} : floor x < z ↔ x < ↑z :=
  lt_iff_lt_of_le_iff_le le_floor

theorem floor_le {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : ↑(floor x) ≤ x :=
  iff.mp le_floor (le_refl (floor x))

theorem floor_nonneg {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {x : α} : 0 ≤ floor x ↔ 0 ≤ x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ floor x ↔ 0 ≤ x)) (propext le_floor))) (iff.refl (↑0 ≤ x))

theorem lt_succ_floor {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : x < ↑(int.succ (floor x)) :=
  iff.mp floor_lt (int.lt_succ_self (floor x))

theorem lt_floor_add_one {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : x < ↑(floor x) + 1 := sorry

theorem sub_one_lt_floor {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : x - 1 < ↑(floor x) :=
  iff.mpr sub_lt_iff_lt_add (lt_floor_add_one x)

@[simp] theorem floor_coe {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (z : ℤ) : floor ↑z = z := sorry

@[simp] theorem floor_zero {α : Type u_1} [linear_ordered_ring α] [floor_ring α] : floor 0 = 0 :=
  floor_coe 0

@[simp] theorem floor_one {α : Type u_1} [linear_ordered_ring α] [floor_ring α] : floor 1 = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (floor 1 = 1)) (Eq.symm int.cast_one)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (floor ↑1 = 1)) (floor_coe 1))) (Eq.refl 1))

theorem floor_mono {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} {b : α} (h : a ≤ b) : floor a ≤ floor b :=
  iff.mpr le_floor (le_trans (floor_le a) h)

@[simp] theorem floor_add_int {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) (z : ℤ) : floor (x + ↑z) = floor x + z := sorry

theorem floor_sub_int {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) (z : ℤ) : floor (x - ↑z) = floor x - z := sorry

theorem abs_sub_lt_one_of_floor_eq_floor {α : Type u_1} [linear_ordered_comm_ring α] [floor_ring α] {x : α} {y : α} (h : floor x = floor y) : abs (x - y) < 1 := sorry

theorem floor_eq_iff {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {r : α} {z : ℤ} : floor r = z ↔ ↑z ≤ r ∧ r < ↑z + 1 := sorry

theorem floor_ring_unique {α : Type u_1} [linear_ordered_ring α] (inst1 : floor_ring α) (inst2 : floor_ring α) : floor = floor := sorry

theorem floor_eq_on_Ico {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (n : ℤ) (x : α) (H : x ∈ set.Ico (↑n) (↑n + 1)) : floor x = n := sorry

theorem floor_eq_on_Ico' {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (n : ℤ) (x : α) (H : x ∈ set.Ico (↑n) (↑n + 1)) : ↑(floor x) = ↑n :=
  eq.mpr (id (propext int.cast_inj)) (floor_eq_on_Ico n x hx)

/-- The fractional part fract r of r is just r - ⌊r⌋ -/
def fract {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : α :=
  r - ↑(floor r)

-- Mathematical notation is usually {r}. Let's not even go there.

@[simp] theorem floor_add_fract {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : ↑(floor r) + fract r = r := sorry

@[simp] theorem fract_add_floor {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : fract r + ↑(floor r) = r :=
  sub_add_cancel r ↑(floor r)

theorem fract_nonneg {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : 0 ≤ fract r :=
  iff.mpr sub_nonneg (floor_le r)

theorem fract_lt_one {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : fract r < 1 :=
  iff.mp sub_lt (sub_one_lt_floor r)

@[simp] theorem fract_zero {α : Type u_1} [linear_ordered_ring α] [floor_ring α] : fract 0 = 0 := sorry

@[simp] theorem fract_coe {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (z : ℤ) : fract ↑z = 0 := sorry

@[simp] theorem fract_floor {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : fract ↑(floor r) = 0 :=
  fract_coe (floor r)

@[simp] theorem floor_fract {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : floor (fract r) = 0 := sorry

theorem fract_eq_iff {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {r : α} {s : α} : fract r = s ↔ 0 ≤ s ∧ s < 1 ∧ ∃ (z : ℤ), r - s = ↑z := sorry

theorem fract_eq_fract {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {r : α} {s : α} : fract r = fract s ↔ ∃ (z : ℤ), r - s = ↑z := sorry

@[simp] theorem fract_fract {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) : fract (fract r) = fract r := sorry

theorem fract_add {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) (s : α) : ∃ (z : ℤ), fract (r + s) - fract r - fract s = ↑z := sorry

theorem fract_mul_nat {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (r : α) (b : ℕ) : ∃ (z : ℤ), fract r * ↑b - fract (r * ↑b) = ↑z := sorry

/-- `ceil x` is the smallest integer `z` such that `x ≤ z` -/
def ceil {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : ℤ :=
  -floor (-x)

theorem ceil_le {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {z : ℤ} {x : α} : ceil x ≤ z ↔ x ≤ ↑z := sorry

theorem lt_ceil {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {x : α} {z : ℤ} : z < ceil x ↔ ↑z < x :=
  lt_iff_lt_of_le_iff_le ceil_le

theorem ceil_le_floor_add_one {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : ceil x ≤ floor x + 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (ceil x ≤ floor x + 1)) (propext ceil_le)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (x ≤ ↑(floor x + 1))) (int.cast_add (floor x) 1)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (x ≤ ↑(floor x) + ↑1)) int.cast_one)) (le_of_lt (lt_floor_add_one x))))

theorem le_ceil {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : x ≤ ↑(ceil x) :=
  iff.mp ceil_le (le_refl (ceil x))

@[simp] theorem ceil_coe {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (z : ℤ) : ceil ↑z = z := sorry

theorem ceil_mono {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} {b : α} (h : a ≤ b) : ceil a ≤ ceil b :=
  iff.mpr ceil_le (le_trans h (le_ceil b))

@[simp] theorem ceil_add_int {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) (z : ℤ) : ceil (x + ↑z) = ceil x + z := sorry

theorem ceil_sub_int {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) (z : ℤ) : ceil (x - ↑z) = ceil x - z := sorry

theorem ceil_lt_add_one {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (x : α) : ↑(ceil x) < x + 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(ceil x) < x + 1)) (Eq.symm (propext lt_ceil))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (ceil x < ceil (x + 1))) (Eq.symm int.cast_one)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (ceil x < ceil (x + ↑1))) (ceil_add_int x 1))) (lt_add_one (ceil x))))

theorem ceil_pos {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} : 0 < ceil a ↔ 0 < a := sorry

@[simp] theorem ceil_zero {α : Type u_1} [linear_ordered_ring α] [floor_ring α] : ceil 0 = 0 := sorry

theorem ceil_nonneg {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {q : α} (hq : 0 ≤ q) : 0 ≤ ceil q := sorry

theorem ceil_eq_iff {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {r : α} {z : ℤ} : ceil r = z ↔ ↑z - 1 < r ∧ r ≤ ↑z := sorry

theorem ceil_eq_on_Ioc {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (n : ℤ) (x : α) (H : x ∈ set.Ioc (↑n - 1) ↑n) : ceil x = n := sorry

theorem ceil_eq_on_Ioc' {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (n : ℤ) (x : α) (H : x ∈ set.Ioc (↑n - 1) ↑n) : ↑(ceil x) = ↑n :=
  eq.mpr (id (propext int.cast_inj)) (ceil_eq_on_Ioc n x hx)

/--
`nat_ceil x` is the smallest nonnegative integer `n` with `x ≤ n`.
It is the same as `⌈q⌉` when `q ≥ 0`, otherwise it is `0`.
-/
def nat_ceil {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (a : α) : ℕ :=
  int.to_nat (ceil a)

theorem nat_ceil_le {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} {n : ℕ} : nat_ceil a ≤ n ↔ a ≤ ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_ceil a ≤ n ↔ a ≤ ↑n)) (nat_ceil.equations._eqn_1 a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (int.to_nat (ceil a) ≤ n ↔ a ≤ ↑n)) (propext int.to_nat_le)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (ceil a ≤ ↑n ↔ a ≤ ↑n)) (propext ceil_le))) (iff.refl (a ≤ ↑↑n))))

theorem lt_nat_ceil {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} {n : ℕ} : n < nat_ceil a ↔ ↑n < a := sorry

theorem le_nat_ceil {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (a : α) : a ≤ ↑(nat_ceil a) :=
  iff.mp nat_ceil_le (le_refl (nat_ceil a))

theorem nat_ceil_mono {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a₁ : α} {a₂ : α} (h : a₁ ≤ a₂) : nat_ceil a₁ ≤ nat_ceil a₂ :=
  iff.mpr nat_ceil_le (le_trans h (le_nat_ceil a₂))

@[simp] theorem nat_ceil_coe {α : Type u_1} [linear_ordered_ring α] [floor_ring α] (n : ℕ) : nat_ceil ↑n = n :=
  (fun (this : int.to_nat (ceil ↑↑n) = n) => this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (int.to_nat (ceil ↑↑n) = n)) (ceil_coe ↑n))) (Eq.refl (int.to_nat ↑n)))

@[simp] theorem nat_ceil_zero {α : Type u_1} [linear_ordered_ring α] [floor_ring α] : nat_ceil 0 = 0 :=
  nat_ceil_coe 0

theorem nat_ceil_add_nat {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} (a_nonneg : 0 ≤ a) (n : ℕ) : nat_ceil (a + ↑n) = nat_ceil a + n := sorry

theorem nat_ceil_lt_add_one {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {a : α} (a_nonneg : 0 ≤ a) : ↑(nat_ceil a) < a + 1 := sorry

theorem lt_of_nat_ceil_lt {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {x : α} {n : ℕ} (h : nat_ceil x < n) : x < ↑n :=
  lt_of_le_of_lt (le_nat_ceil x) (eq.mpr (id (propext nat.cast_lt)) h)

theorem le_of_nat_ceil_le {α : Type u_1} [linear_ordered_ring α] [floor_ring α] {x : α} {n : ℕ} (h : nat_ceil x ≤ n) : x ≤ ↑n :=
  le_trans (le_nat_ceil x) (eq.mpr (id (propext nat.cast_le)) h)


import WaterSortPuzzle.Defs

nsmeapce
/--
给定k的求解
-/
def solveAux (config : PuzzleConfig m h n) : Option (Solution n) := sorry

/--
求解函数
-/
partial def solve (config : PuzzleConfig m h n) : Solution n :=
  match solveAux config with
  | some solution => solution
  | none => solve { config with k := config.k + 1 }

/--
证明 k ≥ n 时，一定有解
-/
theorem solveAux_k_ge_n : ∀ {config : PuzzleConfig m h n}, config.k ≥ n → Solution n := sorry