/-
Copyright (c) 2022 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module data.set.pointwise.support
! leanprover-community/mathlib commit 68d1483e8a718ec63219f0e227ca3f0140361086
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Pointwise.Smul
import Mathbin.Algebra.Support

/-!
# Support of a function composed with a scalar action

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

We show that the support of `x ↦ f (c⁻¹ • x)` is equal to `c • support f`.
-/

open Pointwise

open Function Set

section Group

variable {α β γ : Type _} [Group α] [MulAction α β]

/- warning: mul_support_comp_inv_smul -> mulSupport_comp_inv_smul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] [_inst_3 : One.{u3} γ] (c : α) (f : β -> γ), Eq.{succ u2} (Set.{u2} β) (Function.mulSupport.{u2, u3} β γ _inst_3 (fun (x : β) => f (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2) (Inv.inv.{u1} α (DivInvMonoid.toHasInv.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) c) x))) (SMul.smul.{u1, u2} α (Set.{u2} β) (Set.smulSet.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) c (Function.mulSupport.{u2, u3} β γ _inst_3 f))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] [_inst_3 : One.{u3} γ] (c : α) (f : β -> γ), Eq.{succ u2} (Set.{u2} β) (Function.mulSupport.{u2, u3} β γ _inst_3 (fun (x : β) => f (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) (Inv.inv.{u1} α (InvOneClass.toInv.{u1} α (DivInvOneMonoid.toInvOneClass.{u1} α (DivisionMonoid.toDivInvOneMonoid.{u1} α (Group.toDivisionMonoid.{u1} α _inst_1)))) c) x))) (HSMul.hSMul.{u1, u2, u2} α (Set.{u2} β) (Set.{u2} β) (instHSMul.{u1, u2} α (Set.{u2} β) (Set.smulSet.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2))) c (Function.mulSupport.{u2, u3} β γ _inst_3 f))
Case conversion may be inaccurate. Consider using '#align mul_support_comp_inv_smul mulSupport_comp_inv_smulₓ'. -/
theorem mulSupport_comp_inv_smul [One γ] (c : α) (f : β → γ) :
    (mulSupport fun x => f (c⁻¹ • x)) = c • mulSupport f :=
  by
  ext x
  simp only [mem_smul_set_iff_inv_smul_mem, mem_mul_support]
#align mul_support_comp_inv_smul mulSupport_comp_inv_smul

/- warning: support_comp_inv_smul -> support_comp_inv_smul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] [_inst_3 : Zero.{u3} γ] (c : α) (f : β -> γ), Eq.{succ u2} (Set.{u2} β) (Function.support.{u2, u3} β γ _inst_3 (fun (x : β) => f (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2) (Inv.inv.{u1} α (DivInvMonoid.toHasInv.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) c) x))) (SMul.smul.{u1, u2} α (Set.{u2} β) (Set.smulSet.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) c (Function.support.{u2, u3} β γ _inst_3 f))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] [_inst_3 : Zero.{u3} γ] (c : α) (f : β -> γ), Eq.{succ u2} (Set.{u2} β) (Function.support.{u2, u3} β γ _inst_3 (fun (x : β) => f (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) (Inv.inv.{u1} α (InvOneClass.toInv.{u1} α (DivInvOneMonoid.toInvOneClass.{u1} α (DivisionMonoid.toDivInvOneMonoid.{u1} α (Group.toDivisionMonoid.{u1} α _inst_1)))) c) x))) (HSMul.hSMul.{u1, u2, u2} α (Set.{u2} β) (Set.{u2} β) (instHSMul.{u1, u2} α (Set.{u2} β) (Set.smulSet.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2))) c (Function.support.{u2, u3} β γ _inst_3 f))
Case conversion may be inaccurate. Consider using '#align support_comp_inv_smul support_comp_inv_smulₓ'. -/
theorem support_comp_inv_smul [Zero γ] (c : α) (f : β → γ) :
    (support fun x => f (c⁻¹ • x)) = c • support f :=
  by
  ext x
  simp only [mem_smul_set_iff_inv_smul_mem, mem_support]
#align support_comp_inv_smul support_comp_inv_smul

attribute [to_additive support_comp_inv_smul] mulSupport_comp_inv_smul

end Group

section GroupWithZero

variable {α β γ : Type _} [GroupWithZero α] [MulAction α β]

/- warning: mul_support_comp_inv_smul₀ -> mulSupport_comp_inv_smul₀ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : GroupWithZero.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (MonoidWithZero.toMonoid.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1))] [_inst_3 : One.{u3} γ] {c : α}, (Ne.{succ u1} α c (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (MulZeroOneClass.toMulZeroClass.{u1} α (MonoidWithZero.toMulZeroOneClass.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1)))))))) -> (forall (f : β -> γ), Eq.{succ u2} (Set.{u2} β) (Function.mulSupport.{u2, u3} β γ _inst_3 (fun (x : β) => f (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (MonoidWithZero.toMonoid.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1)) _inst_2) (Inv.inv.{u1} α (DivInvMonoid.toHasInv.{u1} α (GroupWithZero.toDivInvMonoid.{u1} α _inst_1)) c) x))) (SMul.smul.{u1, u2} α (Set.{u2} β) (Set.smulSet.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (MonoidWithZero.toMonoid.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1)) _inst_2)) c (Function.mulSupport.{u2, u3} β γ _inst_3 f)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : GroupWithZero.{u2} α] [_inst_2 : MulAction.{u2, u1} α β (MonoidWithZero.toMonoid.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1))] [_inst_3 : One.{u3} γ] {c : α}, (Ne.{succ u2} α c (OfNat.ofNat.{u2} α 0 (Zero.toOfNat0.{u2} α (MonoidWithZero.toZero.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1))))) -> (forall (f : β -> γ), Eq.{succ u1} (Set.{u1} β) (Function.mulSupport.{u1, u3} β γ _inst_3 (fun (x : β) => f (HSMul.hSMul.{u2, u1, u1} α β β (instHSMul.{u2, u1} α β (MulAction.toSMul.{u2, u1} α β (MonoidWithZero.toMonoid.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1)) _inst_2)) (Inv.inv.{u2} α (GroupWithZero.toInv.{u2} α _inst_1) c) x))) (HSMul.hSMul.{u2, u1, u1} α (Set.{u1} β) (Set.{u1} β) (instHSMul.{u2, u1} α (Set.{u1} β) (Set.smulSet.{u2, u1} α β (MulAction.toSMul.{u2, u1} α β (MonoidWithZero.toMonoid.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1)) _inst_2))) c (Function.mulSupport.{u1, u3} β γ _inst_3 f)))
Case conversion may be inaccurate. Consider using '#align mul_support_comp_inv_smul₀ mulSupport_comp_inv_smul₀ₓ'. -/
theorem mulSupport_comp_inv_smul₀ [One γ] {c : α} (hc : c ≠ 0) (f : β → γ) :
    (mulSupport fun x => f (c⁻¹ • x)) = c • mulSupport f :=
  by
  ext x
  simp only [mem_smul_set_iff_inv_smul_mem₀ hc, mem_mul_support]
#align mul_support_comp_inv_smul₀ mulSupport_comp_inv_smul₀

/- warning: support_comp_inv_smul₀ -> support_comp_inv_smul₀ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : GroupWithZero.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (MonoidWithZero.toMonoid.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1))] [_inst_3 : Zero.{u3} γ] {c : α}, (Ne.{succ u1} α c (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (MulZeroOneClass.toMulZeroClass.{u1} α (MonoidWithZero.toMulZeroOneClass.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1)))))))) -> (forall (f : β -> γ), Eq.{succ u2} (Set.{u2} β) (Function.support.{u2, u3} β γ _inst_3 (fun (x : β) => f (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (MonoidWithZero.toMonoid.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1)) _inst_2) (Inv.inv.{u1} α (DivInvMonoid.toHasInv.{u1} α (GroupWithZero.toDivInvMonoid.{u1} α _inst_1)) c) x))) (SMul.smul.{u1, u2} α (Set.{u2} β) (Set.smulSet.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (MonoidWithZero.toMonoid.{u1} α (GroupWithZero.toMonoidWithZero.{u1} α _inst_1)) _inst_2)) c (Function.support.{u2, u3} β γ _inst_3 f)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : GroupWithZero.{u2} α] [_inst_2 : MulAction.{u2, u1} α β (MonoidWithZero.toMonoid.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1))] [_inst_3 : Zero.{u3} γ] {c : α}, (Ne.{succ u2} α c (OfNat.ofNat.{u2} α 0 (Zero.toOfNat0.{u2} α (MonoidWithZero.toZero.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1))))) -> (forall (f : β -> γ), Eq.{succ u1} (Set.{u1} β) (Function.support.{u1, u3} β γ _inst_3 (fun (x : β) => f (HSMul.hSMul.{u2, u1, u1} α β β (instHSMul.{u2, u1} α β (MulAction.toSMul.{u2, u1} α β (MonoidWithZero.toMonoid.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1)) _inst_2)) (Inv.inv.{u2} α (GroupWithZero.toInv.{u2} α _inst_1) c) x))) (HSMul.hSMul.{u2, u1, u1} α (Set.{u1} β) (Set.{u1} β) (instHSMul.{u2, u1} α (Set.{u1} β) (Set.smulSet.{u2, u1} α β (MulAction.toSMul.{u2, u1} α β (MonoidWithZero.toMonoid.{u2} α (GroupWithZero.toMonoidWithZero.{u2} α _inst_1)) _inst_2))) c (Function.support.{u1, u3} β γ _inst_3 f)))
Case conversion may be inaccurate. Consider using '#align support_comp_inv_smul₀ support_comp_inv_smul₀ₓ'. -/
theorem support_comp_inv_smul₀ [Zero γ] {c : α} (hc : c ≠ 0) (f : β → γ) :
    (support fun x => f (c⁻¹ • x)) = c • support f :=
  by
  ext x
  simp only [mem_smul_set_iff_inv_smul_mem₀ hc, mem_support]
#align support_comp_inv_smul₀ support_comp_inv_smul₀

attribute [to_additive support_comp_inv_smul₀] mulSupport_comp_inv_smul₀

end GroupWithZero


namespace one
variable U : Type
variable P : U → Prop

example (x : U) (h : P x) : ∃ x, P x :=
exists.intro x h
end one

namespace two
variable U : Type
variable P : U → Prop
variable Q : Prop

example (h1 : ∃ x, P x) (h2 : ∀ x, P x → Q) : Q :=
exists.elim h1
  (assume (y : U) (h : P y),
    have h3 : P y → Q, from h2 y,
    show Q, from h3 h)
end two

-- More simple example
namespace three
variable U : Type
variable P : U → Prop
variable a : Prop

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

example (h : ∃ x, p x) : ∃ x, p x  :=
exists.elim h
  (assume w,
    assume hw : p w ,
    show ∃ x, p x, from ⟨w, hw⟩)
end three


lemma contrapositive2 (P Q : Prop) : (¬ Q → ¬ P) → (P → Q) :=
begin
by_cases p : P; by_cases q : Q,
repeat {cc},
end

-- solution without cc
-- lemma contrapositive2 (P Q : Prop) : (¬ Q → ¬ P) → (P → Q) :=
-- begin
-- by_cases p : P; by_cases q : Q,
-- intros h1 h2,
-- exact q,
-- intro h,
-- exfalso,
-- exact h(q)(p),
-- intros h1 h2,
-- exact q,
-- intros h1 h2,
-- exfalso,
-- exact p(h2),
-- end


/-
Copyright (c) 2021 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers
-/
import linear_algebra.ray
import linear_algebra.determinant

/-!
# Orientations of modules

This file defines orientations of modules.

## Main definitions

* `orientation` is a type synonym for `module.ray` for the case where the module is that of
alternating maps from a module to its underlying ring.  An orientation may be associated with an
alternating map or with a basis.

* `module.oriented` is a type class for a choice of orientation of a module that is considered
the positive orientation.

## Implementation notes

`orientation` is defined for an arbitrary index type, but the main intended use case is when
that index type is a `fintype` and there exists a basis of the same cardinality.

## References

* https://en.wikipedia.org/wiki/Orientation_(vector_space)

-/

noncomputable theory

open_locale big_operators

section ordered_comm_semiring

variables (R : Type*) [strict_ordered_comm_semiring R]
variables (M : Type*) [add_comm_monoid M] [module R M]
variables {N : Type*} [add_comm_monoid N] [module R N]
variables (ι : Type*)

/-- An orientation of a module, intended to be used when `ι` is a `fintype` with the same
cardinality as a basis. -/
abbreviation orientation := module.ray R (alternating_map R M R ι)

/-- A type class fixing an orientation of a module. -/
class module.oriented :=
(positive_orientation : orientation R M ι)

export module.oriented (positive_orientation)

variables {R M}

/-- An equivalence between modules implies an equivalence between orientations. -/
def orientation.map (e : M ≃ₗ[R] N) : orientation R M ι ≃ orientation R N ι :=
module.ray.map $ alternating_map.dom_lcongr R R ι R e

@[simp] lemma orientation.map_apply (e : M ≃ₗ[R] N) (v : alternating_map R M R ι)
  (hv : v ≠ 0) :
  orientation.map ι e (ray_of_ne_zero _ v hv) = ray_of_ne_zero _ (v.comp_linear_map e.symm)
      (mt (v.comp_linear_equiv_eq_zero_iff e.symm).mp hv) := rfl

@[simp] lemma orientation.map_refl :
  (orientation.map ι $ linear_equiv.refl R M) = equiv.refl _ :=
by rw [orientation.map, alternating_map.dom_lcongr_refl, module.ray.map_refl]

@[simp] lemma orientation.map_symm (e : M ≃ₗ[R] N) :
  (orientation.map ι e).symm = orientation.map ι e.symm := rfl

/-- A module is canonically oriented with respect to an empty index type. -/
@[priority 100] instance is_empty.oriented [nontrivial R] [is_empty ι] :
  module.oriented R M ι :=
{ positive_orientation := ray_of_ne_zero R (alternating_map.const_linear_equiv_of_is_empty 1) $
    alternating_map.const_linear_equiv_of_is_empty.injective.ne (by simp) }

@[simp] lemma orientation.map_positive_orientation_of_is_empty [nontrivial R] [is_empty ι]
  (f : M ≃ₗ[R] N) :
  orientation.map ι f positive_orientation = positive_orientation :=
rfl

@[simp] lemma orientation.map_of_is_empty [is_empty ι] (x : orientation R M ι) (f : M ≃ₗ[R] M) :
  orientation.map ι f x = x :=
begin
  induction x using module.ray.ind with g hg,
  rw orientation.map_apply,
  congr,
  ext i,
  rw alternating_map.comp_linear_map_apply,
  congr,
end

end ordered_comm_semiring

section ordered_comm_ring

variables {R : Type*} [strict_ordered_comm_ring R]
variables {M N : Type*} [add_comm_group M] [add_comm_group N] [module R M] [module R N]

@[simp] protected lemma orientation.map_neg {ι : Type*} (f : M ≃ₗ[R] N)
  (x : orientation R M ι) :
  orientation.map ι f (-x) = - orientation.map ι f x :=
module.ray.map_neg _ x

namespace basis

variables {ι : Type*}

/-- The value of `orientation.map` when the index type has the cardinality of a basis, in terms
of `f.det`. -/
lemma map_orientation_eq_det_inv_smul [finite ι] (e : basis ι R M)
  (x : orientation R M ι) (f : M ≃ₗ[R] M) : orientation.map ι f x = (f.det)⁻¹ • x :=
begin
  casesI nonempty_fintype ι,
  letI := classical.dec_eq ι,
  induction x using module.ray.ind with g hg,
  rw [orientation.map_apply, smul_ray_of_ne_zero, ray_eq_iff, units.smul_def,
      (g.comp_linear_map ↑f.symm).eq_smul_basis_det e, g.eq_smul_basis_det e,
      alternating_map.comp_linear_map_apply, alternating_map.smul_apply, basis.det_comp,
      basis.det_self, mul_one, smul_eq_mul, mul_comm, mul_smul, linear_equiv.coe_inv_det],
end

variables [fintype ι] [decidable_eq ι]

/-- The orientation given by a basis. -/
protected def orientation [nontrivial R] (e : basis ι R M) : orientation R M ι :=
ray_of_ne_zero R _ e.det_ne_zero

lemma orientation_map [nontrivial R] (e : basis ι R M)
  (f : M ≃ₗ[R] N) : (e.map f).orientation = orientation.map ι f e.orientation :=
by simp_rw [basis.orientation, orientation.map_apply, basis.det_map']

/-- The orientation given by a basis derived using `units_smul`, in terms of the product of those
units. -/
lemma orientation_units_smul [nontrivial R] (e : basis ι R M) (w : ι → units R) :
  (e.units_smul w).orientation = (∏ i, w i)⁻¹ • e.orientation :=
begin
  rw [basis.orientation, basis.orientation, smul_ray_of_ne_zero, ray_eq_iff,
      e.det.eq_smul_basis_det (e.units_smul w), det_units_smul_self, units.smul_def, smul_smul],
  norm_cast,
  simp
end

@[simp] lemma orientation_is_empty [nontrivial R] [is_empty ι] (b : basis ι R M) :
  b.orientation = positive_orientation :=
begin
  congrm ray_of_ne_zero _ _ _,
  convert b.det_is_empty,
end

end basis

end ordered_comm_ring

section linear_ordered_comm_ring

variables {R : Type*} [linear_ordered_comm_ring R]
variables {M : Type*} [add_comm_group M] [module R M]
variables {ι : Type*}

namespace orientation

/-- A module `M` over a linearly ordered commutative ring has precisely two "orientations" with
respect to an empty index type. (Note that these are only orientations of `M` of in the conventional
mathematical sense if `M` is zero-dimensional.) -/
lemma eq_or_eq_neg_of_is_empty [nontrivial R] [is_empty ι] (o : orientation R M ι) :
  o = positive_orientation ∨ o = - positive_orientation :=
begin
  induction o using module.ray.ind with x hx,
  dsimp [positive_orientation],
  simp only [ray_eq_iff, same_ray_neg_swap],
  rw same_ray_or_same_ray_neg_iff_not_linear_independent,
  intros h,
  let a : R := alternating_map.const_linear_equiv_of_is_empty.symm x,
  have H : linear_independent R ![a, 1],
  { convert h.map' ↑alternating_map.const_linear_equiv_of_is_empty.symm (linear_equiv.ker _),
    ext i,
    fin_cases i;
    simp [a] },
  rw linear_independent_iff' at H,
  simpa using H finset.univ ![1, -a] (by simp [fin.sum_univ_succ]) 0 (by simp),
end

end orientation

namespace basis

variables [fintype ι] [decidable_eq ι]

/-- The orientations given by two bases are equal if and only if the determinant of one basis
with respect to the other is positive. -/
lemma orientation_eq_iff_det_pos (e₁ e₂ : basis ι R M) :
  e₁.orientation = e₂.orientation ↔ 0 < e₁.det e₂ :=
calc e₁.orientation = e₂.orientation ↔ same_ray R e₁.det e₂.det : ray_eq_iff _ _
... ↔ same_ray R (e₁.det e₂ • e₂.det) e₂.det : by rw [← e₁.det.eq_smul_basis_det e₂]
... ↔ 0 < e₁.det e₂ : same_ray_smul_left_iff_of_ne e₂.det_ne_zero (e₁.is_unit_det e₂).ne_zero

/-- Given a basis, any orientation equals the orientation given by that basis or its negation. -/
lemma orientation_eq_or_eq_neg (e : basis ι R M) (x : orientation R M ι) :
  x = e.orientation ∨ x = -e.orientation :=
begin
  induction x using module.ray.ind with x hx,
  rw ← x.map_basis_ne_zero_iff e at hx,
  rwa [basis.orientation, ray_eq_iff, neg_ray_of_ne_zero, ray_eq_iff, x.eq_smul_basis_det e,
    same_ray_neg_smul_left_iff_of_ne e.det_ne_zero hx,
    same_ray_smul_left_iff_of_ne e.det_ne_zero hx, lt_or_lt_iff_ne, ne_comm]
end

/-- Given a basis, an orientation equals the negation of that given by that basis if and only
if it does not equal that given by that basis. -/
lemma orientation_ne_iff_eq_neg (e : basis ι R M) (x : orientation R M ι) :
  x ≠ e.orientation ↔ x = -e.orientation :=
⟨λ h, (e.orientation_eq_or_eq_neg x).resolve_left h,
 λ h, h.symm ▸ (module.ray.ne_neg_self e.orientation).symm⟩

/-- Composing a basis with a linear equiv gives the same orientation if and only if the
determinant is positive. -/
lemma orientation_comp_linear_equiv_eq_iff_det_pos (e : basis ι R M) (f : M ≃ₗ[R] M) :
  (e.map f).orientation = e.orientation ↔ 0 < (f : M →ₗ[R] M).det :=
by rw [orientation_map, e.map_orientation_eq_det_inv_smul, units_inv_smul, units_smul_eq_self_iff,
  linear_equiv.coe_det]

/-- Composing a basis with a linear equiv gives the negation of that orientation if and only if
the determinant is negative. -/
lemma orientation_comp_linear_equiv_eq_neg_iff_det_neg (e : basis ι R M) (f : M ≃ₗ[R] M) :
  (e.map f).orientation = -e.orientation ↔ (f : M →ₗ[R] M).det < 0 :=
by rw [orientation_map, e.map_orientation_eq_det_inv_smul, units_inv_smul, units_smul_eq_neg_iff,
  linear_equiv.coe_det]

/-- Negating a single basis vector (represented using `units_smul`) negates the corresponding
orientation. -/
@[simp] lemma orientation_neg_single [nontrivial R] (e : basis ι R M) (i : ι) :
  (e.units_smul (function.update 1 i (-1))).orientation = -e.orientation :=
begin
  rw [orientation_units_smul, finset.prod_update_of_mem (finset.mem_univ _)],
  simp
end

/-- Given a basis and an orientation, return a basis giving that orientation: either the original
basis, or one constructed by negating a single (arbitrary) basis vector. -/
def adjust_to_orientation [nontrivial R] [nonempty ι] (e : basis ι R M) (x : orientation R M ι) :
  basis ι R M :=
by haveI := classical.dec_eq (orientation R M ι); exact if e.orientation = x then e else
  (e.units_smul (function.update 1 (classical.arbitrary ι) (-1)))

/-- `adjust_to_orientation` gives a basis with the required orientation. -/
@[simp] lemma orientation_adjust_to_orientation [nontrivial R] [nonempty ι] (e : basis ι R M)
  (x : orientation R M ι) : (e.adjust_to_orientation x).orientation = x :=
begin
  rw adjust_to_orientation,
  split_ifs with h,
  { exact h },
  { rw [orientation_neg_single, eq_comm, ←orientation_ne_iff_eq_neg, ne_comm],
    exact h }
end

/-- Every basis vector from `adjust_to_orientation` is either that from the original basis or its
negation. -/
lemma adjust_to_orientation_apply_eq_or_eq_neg [nontrivial R] [nonempty ι] (e : basis ι R M)
  (x : orientation R M ι) (i : ι) :
  e.adjust_to_orientation x i = e i ∨ e.adjust_to_orientation x i = -(e i) :=
begin
  rw adjust_to_orientation,
  split_ifs with h,
  { simp },
  { by_cases hi : i = classical.arbitrary ι;
      simp [units_smul_apply, hi] }
end

lemma det_adjust_to_orientation [nontrivial R] [nonempty ι] (e : basis ι R M)
  (x : orientation R M ι) :
  (e.adjust_to_orientation x).det = e.det ∨ (e.adjust_to_orientation x).det = - e.det :=
begin
  dsimp [basis.adjust_to_orientation],
  split_ifs,
  { left,
    refl },
  { right,
    simp [e.det_units_smul, ← units.coe_prod, finset.prod_update_of_mem] }
end

@[simp] lemma abs_det_adjust_to_orientation [nontrivial R] [nonempty ι] (e : basis ι R M)
  (x : orientation R M ι) (v : ι → M) :
  |(e.adjust_to_orientation x).det v| = |e.det v| :=
begin
  cases e.det_adjust_to_orientation x with h h;
  simp [h]
end

end basis

end linear_ordered_comm_ring

section linear_ordered_field

variables {R : Type*} [linear_ordered_field R]
variables {M : Type*} [add_comm_group M] [module R M]
variables {ι : Type*}

namespace orientation

variables [fintype ι] [_i : finite_dimensional R M]

open finite_dimensional

include _i

/-- If the index type has cardinality equal to the finite dimension, any two orientations are
equal or negations. -/
lemma eq_or_eq_neg (x₁ x₂ : orientation R M ι) (h : fintype.card ι = finrank R M) :
  x₁ = x₂ ∨ x₁ = -x₂ :=
begin
  have e := (fin_basis R M).reindex (fintype.equiv_fin_of_card_eq h).symm,
  letI := classical.dec_eq ι,
  rcases e.orientation_eq_or_eq_neg x₁ with h₁|h₁;
    rcases e.orientation_eq_or_eq_neg x₂ with h₂|h₂;
    simp [h₁, h₂]
end

/-- If the index type has cardinality equal to the finite dimension, an orientation equals the
negation of another orientation if and only if they are not equal. -/
lemma ne_iff_eq_neg (x₁ x₂ : orientation R M ι) (h : fintype.card ι = finrank R M) :
  x₁ ≠ x₂ ↔ x₁ = -x₂ :=
⟨λ hn, (eq_or_eq_neg x₁ x₂ h).resolve_left hn, λ he, he.symm ▸ (module.ray.ne_neg_self x₂).symm⟩

/-- The value of `orientation.map` when the index type has cardinality equal to the finite
dimension, in terms of `f.det`. -/
lemma map_eq_det_inv_smul (x : orientation R M ι) (f : M ≃ₗ[R] M)
  (h : fintype.card ι = finrank R M) :
  orientation.map ι f x = (f.det)⁻¹ • x :=
begin
  have e := (fin_basis R M).reindex (fintype.equiv_fin_of_card_eq h).symm,
  exact e.map_orientation_eq_det_inv_smul x f
end

omit _i

/-- If the index type has cardinality equal to the finite dimension, composing an alternating
map with the same linear equiv on each argument gives the same orientation if and only if the
determinant is positive. -/
lemma map_eq_iff_det_pos (x : orientation R M ι) (f : M ≃ₗ[R] M)
  (h : fintype.card ι = finrank R M) :
  orientation.map ι f x = x ↔ 0 < (f : M →ₗ[R] M).det :=
begin
  casesI is_empty_or_nonempty ι,
  { have H : finrank R M = 0,
    { refine h.symm.trans _,
      convert fintype.card_of_is_empty,
      apply_instance },
    simp [linear_map.det_eq_one_of_finrank_eq_zero H] },
  have H : 0 < finrank R M,
  { rw ← h,
    exact fintype.card_pos },
  haveI : finite_dimensional R M := finite_dimensional_of_finrank H,
  rw [map_eq_det_inv_smul _ _ h, units_inv_smul, units_smul_eq_self_iff, linear_equiv.coe_det]
end

/-- If the index type has cardinality equal to the finite dimension, composing an alternating
map with the same linear equiv on each argument gives the negation of that orientation if and
only if the determinant is negative. -/
lemma map_eq_neg_iff_det_neg (x : orientation R M ι) (f : M ≃ₗ[R] M)
  (h : fintype.card ι = finrank R M) :
  orientation.map ι f x = -x ↔ (f : M →ₗ[R] M).det < 0 :=
begin
  casesI is_empty_or_nonempty ι,
  { have H : finrank R M = 0,
    { refine h.symm.trans _,
      convert fintype.card_of_is_empty,
      apply_instance },
    simp [linear_map.det_eq_one_of_finrank_eq_zero H, module.ray.ne_neg_self x] },
  have H : 0 < finrank R M,
  { rw ← h,
    exact fintype.card_pos },
  haveI : finite_dimensional R M := finite_dimensional_of_finrank H,
  rw [map_eq_det_inv_smul _ _ h, units_inv_smul, units_smul_eq_neg_iff, linear_equiv.coe_det]
end

include _i

/-- If the index type has cardinality equal to the finite dimension, a basis with the given
orientation. -/
def some_basis [nonempty ι] [decidable_eq ι] (x : orientation R M ι)
  (h : fintype.card ι = finrank R M) :
  basis ι R M :=
((fin_basis R M).reindex (fintype.equiv_fin_of_card_eq h).symm).adjust_to_orientation x

/-- `some_basis` gives a basis with the required orientation. -/
@[simp] lemma some_basis_orientation [nonempty ι] [decidable_eq ι] (x : orientation R M ι)
  (h : fintype.card ι = finrank R M) : (x.some_basis h).orientation = x :=
basis.orientation_adjust_to_orientation _ _

end orientation

end linear_ordered_field


def f (m n : ℕ) : ℕ := m + n + m

example {m n : ℕ} (h : n = 1) (h' : 0 = m) : (f m n) = n :=
by simp [h, h'.symm, f]


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

universes u v u_1 l

namespace Mathlib

/-!
# Theory of Cauchy filters in uniform spaces. Complete uniform spaces. Totally bounded subsets.
-/

/-- A filter `f` is Cauchy if for every entourage `r`, there exists an
  `s ∈ f` such that `s × s ⊆ r`. This is a generalization of Cauchy
  sequences, because if `a : ℕ → α` then the filter of sets containing
  cofinitely many of the `a n` is Cauchy iff `a` is a Cauchy sequence. -/
def cauchy {α : Type u} [uniform_space α] (f : filter α) :=
  filter.ne_bot f ∧ filter.prod f f ≤ uniformity α

/-- A set `s` is called *complete*, if any Cauchy filter `f` such that `s ∈ f`
has a limit in `s` (formally, it satisfies `f ≤ 𝓝 x` for some `x ∈ s`). -/
def is_complete {α : Type u} [uniform_space α] (s : set α) :=
  ∀ (f : filter α), cauchy f → f ≤ filter.principal s → ∃ (x : α), ∃ (H : x ∈ s), f ≤ nhds x

theorem filter.has_basis.cauchy_iff {α : Type u} {β : Type v} [uniform_space α] {p : β → Prop} {s : β → set (α × α)} (h : filter.has_basis (uniformity α) p s) {f : filter α} : cauchy f ↔ filter.ne_bot f ∧ ∀ (i : β), p i → ∃ (t : set α), ∃ (H : t ∈ f), ∀ (x y : α), x ∈ t → y ∈ t → (x, y) ∈ s i := sorry

theorem cauchy_iff' {α : Type u} [uniform_space α] {f : filter α} : cauchy f ↔
  filter.ne_bot f ∧
    ∀ (s : set (α × α)) (H : s ∈ uniformity α), ∃ (t : set α), ∃ (H : t ∈ f), ∀ (x y : α), x ∈ t → y ∈ t → (x, y) ∈ s :=
  filter.has_basis.cauchy_iff (filter.basis_sets (uniformity α))

theorem cauchy_iff {α : Type u} [uniform_space α] {f : filter α} : cauchy f ↔ filter.ne_bot f ∧ ∀ (s : set (α × α)) (H : s ∈ uniformity α), ∃ (t : set α), ∃ (H : t ∈ f), set.prod t t ⊆ s := sorry

theorem cauchy_map_iff {α : Type u} {β : Type v} [uniform_space α] {l : filter β} {f : β → α} : cauchy (filter.map f l) ↔
  filter.ne_bot l ∧
    filter.tendsto (fun (p : β × β) => (f (prod.fst p), f (prod.snd p))) (filter.prod l l) (uniformity α) := sorry

theorem cauchy_map_iff' {α : Type u} {β : Type v} [uniform_space α] {l : filter β} [hl : filter.ne_bot l] {f : β → α} : cauchy (filter.map f l) ↔
  filter.tendsto (fun (p : β × β) => (f (prod.fst p), f (prod.snd p))) (filter.prod l l) (uniformity α) :=
  iff.trans cauchy_map_iff (and_iff_right hl)

theorem cauchy.mono {α : Type u} [uniform_space α] {f : filter α} {g : filter α} [hg : filter.ne_bot g] (h_c : cauchy f) (h_le : g ≤ f) : cauchy g :=
  { left := hg, right := le_trans (filter.prod_mono h_le h_le) (and.right h_c) }

theorem cauchy.mono' {α : Type u} [uniform_space α] {f : filter α} {g : filter α} (h_c : cauchy f) (hg : filter.ne_bot g) (h_le : g ≤ f) : cauchy g :=
  cauchy.mono h_c h_le

theorem cauchy_nhds {α : Type u} [uniform_space α] {a : α} : cauchy (nhds a) := sorry

theorem cauchy_pure {α : Type u} [uniform_space α] {a : α} : cauchy (pure a) :=
  cauchy.mono cauchy_nhds (pure_le_nhds a)

theorem filter.tendsto.cauchy_map {α : Type u} {β : Type v} [uniform_space α] {l : filter β} [filter.ne_bot l] {f : β → α} {a : α} (h : filter.tendsto f l (nhds a)) : cauchy (filter.map f l) :=
  cauchy.mono cauchy_nhds h

/-- The common part of the proofs of `le_nhds_of_cauchy_adhp` and
`sequentially_complete.le_nhds_of_seq_tendsto_nhds`: if for any entourage `s`
one can choose a set `t ∈ f` of diameter `s` such that it contains a point `y`
with `(x, y) ∈ s`, then `f` converges to `x`. -/
theorem le_nhds_of_cauchy_adhp_aux {α : Type u} [uniform_space α] {f : filter α} {x : α} (adhs : ∀ (s : set (α × α)) (H : s ∈ uniformity α),
  ∃ (t : set α), ∃ (H : t ∈ f), set.prod t t ⊆ s ∧ ∃ (y : α), (x, y) ∈ s ∧ y ∈ t) : f ≤ nhds x := sorry

/-- If `x` is an adherent (cluster) point for a Cauchy filter `f`, then it is a limit point
for `f`. -/
theorem le_nhds_of_cauchy_adhp {α : Type u} [uniform_space α] {f : filter α} {x : α} (hf : cauchy f) (adhs : cluster_pt x f) : f ≤ nhds x := sorry

theorem le_nhds_iff_adhp_of_cauchy {α : Type u} [uniform_space α] {f : filter α} {x : α} (hf : cauchy f) : f ≤ nhds x ↔ cluster_pt x f :=
  { mp := fun (h : f ≤ nhds x) => cluster_pt.of_le_nhds' h (and.left hf), mpr := le_nhds_of_cauchy_adhp hf }

theorem cauchy.map {α : Type u} {β : Type v} [uniform_space α] [uniform_space β] {f : filter α} {m : α → β} (hf : cauchy f) (hm : uniform_continuous m) : cauchy (filter.map m f) :=
  { left := filter.ne_bot.map (and.left hf) m,
    right := le_trans (trans_rel_right LessEq filter.prod_map_map_eq (filter.map_mono (and.right hf))) hm }

theorem cauchy.comap {α : Type u} {β : Type v} [uniform_space α] [uniform_space β] {f : filter β} {m : α → β} (hf : cauchy f) (hm : filter.comap (fun (p : α × α) => (m (prod.fst p), m (prod.snd p))) (uniformity β) ≤ uniformity α) [filter.ne_bot (filter.comap m f)] : cauchy (filter.comap m f) :=
  { left := _inst_3,
    right := le_trans (trans_rel_right LessEq filter.prod_comap_comap_eq (filter.comap_mono (and.right hf))) hm }

theorem cauchy.comap' {α : Type u} {β : Type v} [uniform_space α] [uniform_space β] {f : filter β} {m : α → β} (hf : cauchy f) (hm : filter.comap (fun (p : α × α) => (m (prod.fst p), m (prod.snd p))) (uniformity β) ≤ uniformity α) (hb : filter.ne_bot (filter.comap m f)) : cauchy (filter.comap m f) :=
  cauchy.comap hf hm

/-- Cauchy sequences. Usually defined on ℕ, but often it is also useful to say that a function
defined on ℝ is Cauchy at +∞ to deduce convergence. Therefore, we define it in a type class that
is general enough to cover both ℕ and ℝ, which are the main motivating examples. -/
def cauchy_seq {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] (u : β → α) :=
  cauchy (filter.map u filter.at_top)

theorem cauchy_seq.mem_entourage {α : Type u} [uniform_space α] {ι : Type u_1} [Nonempty ι] [linear_order ι] {u : ι → α} (h : cauchy_seq u) {V : set (α × α)} (hV : V ∈ uniformity α) : ∃ (k₀ : ι), ∀ (i j : ι), k₀ ≤ i → k₀ ≤ j → (u i, u j) ∈ V := sorry

theorem filter.tendsto.cauchy_seq {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] [Nonempty β] {f : β → α} {x : α} (hx : filter.tendsto f filter.at_top (nhds x)) : cauchy_seq f :=
  filter.tendsto.cauchy_map hx

theorem cauchy_seq_iff_tendsto {α : Type u} {β : Type v} [uniform_space α] [Nonempty β] [semilattice_sup β] {u : β → α} : cauchy_seq u ↔ filter.tendsto (prod.map u u) filter.at_top (uniformity α) := sorry

/-- If a Cauchy sequence has a convergent subsequence, then it converges. -/
theorem tendsto_nhds_of_cauchy_seq_of_subseq {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] {u : β → α} (hu : cauchy_seq u) {ι : Type u_1} {f : ι → β} {p : filter ι} [filter.ne_bot p] (hf : filter.tendsto f p filter.at_top) {a : α} (ha : filter.tendsto (u ∘ f) p (nhds a)) : filter.tendsto u filter.at_top (nhds a) :=
  le_nhds_of_cauchy_adhp hu (map_cluster_pt_of_comp hf ha)

theorem filter.has_basis.cauchy_seq_iff {α : Type u} {β : Type v} [uniform_space α] {γ : Type u_1} [Nonempty β] [semilattice_sup β] {u : β → α} {p : γ → Prop} {s : γ → set (α × α)} (h : filter.has_basis (uniformity α) p s) : cauchy_seq u ↔ ∀ (i : γ), p i → ∃ (N : β), ∀ (m n : β), m ≥ N → n ≥ N → (u m, u n) ∈ s i := sorry

theorem filter.has_basis.cauchy_seq_iff' {α : Type u} {β : Type v} [uniform_space α] {γ : Type u_1} [Nonempty β] [semilattice_sup β] {u : β → α} {p : γ → Prop} {s : γ → set (α × α)} (H : filter.has_basis (uniformity α) p s) : cauchy_seq u ↔ ∀ (i : γ), p i → ∃ (N : β), ∀ (n : β), n ≥ N → (u n, u N) ∈ s i := sorry

theorem cauchy_seq_of_controlled {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] [Nonempty β] (U : β → set (α × α)) (hU : ∀ (s : set (α × α)), s ∈ uniformity α → ∃ (n : β), U n ⊆ s) {f : β → α} (hf : ∀ {N m n : β}, N ≤ m → N ≤ n → (f m, f n) ∈ U N) : cauchy_seq f := sorry

/-- A complete space is defined here using uniformities. A uniform space
  is complete if every Cauchy filter converges. -/
class complete_space (α : Type u) [uniform_space α]
where
  complete : ∀ {f : filter α}, cauchy f → ∃ (x : α), f ≤ nhds x

theorem complete_univ {α : Type u} [uniform_space α] [complete_space α] : is_complete set.univ := sorry

theorem cauchy_prod {α : Type u} {β : Type v} [uniform_space α] [uniform_space β] {f : filter α} {g : filter β} : cauchy f → cauchy g → cauchy (filter.prod f g) := sorry

protected instance complete_space.prod {α : Type u} {β : Type v} [uniform_space α] [uniform_space β] [complete_space α] [complete_space β] : complete_space (α × β) :=
  complete_space.mk fun (f : filter (α × β)) (hf : cauchy f) => sorry

/--If `univ` is complete, the space is a complete space -/
theorem complete_space_of_is_complete_univ {α : Type u} [uniform_space α] (h : is_complete set.univ) : complete_space α := sorry

theorem complete_space_iff_is_complete_univ {α : Type u} [uniform_space α] : complete_space α ↔ is_complete set.univ :=
  { mp := complete_univ, mpr := complete_space_of_is_complete_univ }

theorem cauchy_iff_exists_le_nhds {α : Type u} [uniform_space α] [complete_space α] {l : filter α} [filter.ne_bot l] : cauchy l ↔ ∃ (x : α), l ≤ nhds x := sorry

theorem cauchy_map_iff_exists_tendsto {α : Type u} {β : Type v} [uniform_space α] [complete_space α] {l : filter β} {f : β → α} [filter.ne_bot l] : cauchy (filter.map f l) ↔ ∃ (x : α), filter.tendsto f l (nhds x) :=
  cauchy_iff_exists_le_nhds

/-- A Cauchy sequence in a complete space converges -/
theorem cauchy_seq_tendsto_of_complete {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] [complete_space α] {u : β → α} (H : cauchy_seq u) : ∃ (x : α), filter.tendsto u filter.at_top (nhds x) :=
  complete_space.complete H

/-- If `K` is a complete subset, then any cauchy sequence in `K` converges to a point in `K` -/
theorem cauchy_seq_tendsto_of_is_complete {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] {K : set α} (h₁ : is_complete K) {u : β → α} (h₂ : ∀ (n : β), u n ∈ K) (h₃ : cauchy_seq u) : ∃ (v : α), ∃ (H : v ∈ K), filter.tendsto u filter.at_top (nhds v) := sorry

theorem cauchy.le_nhds_Lim {α : Type u} [uniform_space α] [complete_space α] [Nonempty α] {f : filter α} (hf : cauchy f) : f ≤ nhds (Lim f) :=
  le_nhds_Lim (complete_space.complete hf)

theorem cauchy_seq.tendsto_lim {α : Type u} {β : Type v} [uniform_space α] [semilattice_sup β] [complete_space α] [Nonempty α] {u : β → α} (h : cauchy_seq u) : filter.tendsto u filter.at_top (nhds (lim filter.at_top u)) :=
  cauchy.le_nhds_Lim h

theorem is_closed.is_complete {α : Type u} [uniform_space α] [complete_space α] {s : set α} (h : is_closed s) : is_complete s := sorry

/-- A set `s` is totally bounded if for every entourage `d` there is a finite
  set of points `t` such that every element of `s` is `d`-near to some element of `t`. -/
def totally_bounded {α : Type u} [uniform_space α] (s : set α) :=
  ∀ (d : set (α × α)) (H : d ∈ uniformity α),
    ∃ (t : set α),
      set.finite t ∧ s ⊆ set.Union fun (y : α) => set.Union fun (H : y ∈ t) => set_of fun (x : α) => (x, y) ∈ d

theorem totally_bounded_iff_subset {α : Type u} [uniform_space α] {s : set α} : totally_bounded s ↔
  ∀ (d : set (α × α)) (H : d ∈ uniformity α),
    ∃ (t : set α),
      ∃ (H : t ⊆ s),
        set.finite t ∧ s ⊆ set.Union fun (y : α) => set.Union fun (H : y ∈ t) => set_of fun (x : α) => (x, y) ∈ d := sorry

theorem totally_bounded_of_forall_symm {α : Type u} [uniform_space α] {s : set α} (h : ∀ (V : set (α × α)) (H : V ∈ uniformity α),
  symmetric_rel V →
    ∃ (t : set α), set.finite t ∧ s ⊆ set.Union fun (y : α) => set.Union fun (H : y ∈ t) => uniform_space.ball y V) : totally_bounded s := sorry

theorem totally_bounded_subset {α : Type u} [uniform_space α] {s₁ : set α} {s₂ : set α} (hs : s₁ ⊆ s₂) (h : totally_bounded s₂) : totally_bounded s₁ := sorry

theorem totally_bounded_empty {α : Type u} [uniform_space α] : totally_bounded ∅ := sorry

/-- The closure of a totally bounded set is totally bounded. -/
theorem totally_bounded.closure {α : Type u} [uniform_space α] {s : set α} (h : totally_bounded s) : totally_bounded (closure s) := sorry

/-- The image of a totally bounded set under a unifromly continuous map is totally bounded. -/
theorem totally_bounded.image {α : Type u} {β : Type v} [uniform_space α] [uniform_space β] {f : α → β} {s : set α} (hs : totally_bounded s) (hf : uniform_continuous f) : totally_bounded (f '' s) := sorry

theorem ultrafilter.cauchy_of_totally_bounded {α : Type u} [uniform_space α] {s : set α} (f : ultrafilter α) (hs : totally_bounded s) (h : ↑f ≤ filter.principal s) : cauchy ↑f := sorry

theorem totally_bounded_iff_filter {α : Type u} [uniform_space α] {s : set α} : totally_bounded s ↔
  ∀ (f : filter α), filter.ne_bot f → f ≤ filter.principal s → ∃ (c : filter α), ∃ (H : c ≤ f), cauchy c := sorry

theorem totally_bounded_iff_ultrafilter {α : Type u} [uniform_space α] {s : set α} : totally_bounded s ↔ ∀ (f : ultrafilter α), ↑f ≤ filter.principal s → cauchy ↑f := sorry

theorem compact_iff_totally_bounded_complete {α : Type u} [uniform_space α] {s : set α} : is_compact s ↔ totally_bounded s ∧ is_complete s := sorry

protected instance complete_of_compact {α : Type u} [uniform_space α] [compact_space α] : complete_space α := sorry

theorem compact_of_totally_bounded_is_closed {α : Type u} [uniform_space α] [complete_space α] {s : set α} (ht : totally_bounded s) (hc : is_closed s) : is_compact s :=
  iff.mpr compact_iff_totally_bounded_complete { left := ht, right := is_closed.is_complete hc }

/-!
### Sequentially complete space

In this section we prove that a uniform space is complete provided that it is sequentially complete
(i.e., any Cauchy sequence converges) and its uniformity filter admits a countable generating set.
In particular, this applies to (e)metric spaces, see the files `topology/metric_space/emetric_space`
and `topology/metric_space/basic`.

More precisely, we assume that there is a sequence of entourages `U_n` such that any other
entourage includes one of `U_n`. Then any Cauchy filter `f` generates a decreasing sequence of
sets `s_n ∈ f` such that `s_n × s_n ⊆ U_n`. Choose a sequence `x_n∈s_n`. It is easy to show
that this is a Cauchy sequence. If this sequence converges to some `a`, then `f ≤ 𝓝 a`. -/

namespace sequentially_complete

/-- An auxiliary sequence of sets approximating a Cauchy filter. -/
def set_seq_aux {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (n : ℕ) : Subtype fun (s : set α) => ∃ (_x : s ∈ f), set.prod s s ⊆ U n :=
  classical.indefinite_description (fun (s : set α) => ∃ (_x : s ∈ f), set.prod s s ⊆ U n) sorry

/-- Given a Cauchy filter `f` and a sequence `U` of entourages, `set_seq` provides
a sequence of monotonically decreasing sets `s n ∈ f` such that `(s n).prod (s n) ⊆ U`. -/
def set_seq {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (n : ℕ) : set α :=
  set.Inter fun (m : ℕ) => set.Inter fun (H : m ∈ set.Iic n) => subtype.val (set_seq_aux hf U_mem m)

theorem set_seq_mem {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (n : ℕ) : set_seq hf U_mem n ∈ f :=
  iff.mpr (filter.bInter_mem_sets (set.finite_le_nat n))
    fun (m : ℕ) (_x : m ∈ set_of fun (i : ℕ) => i ≤ n) => Exists.fst (subtype.property (set_seq_aux hf U_mem m))

theorem set_seq_mono {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) {m : ℕ} {n : ℕ} (h : m ≤ n) : set_seq hf U_mem n ⊆ set_seq hf U_mem m :=
  set.bInter_subset_bInter_left fun (k : ℕ) (hk : k ∈ set.Iic m) => le_trans hk h

theorem set_seq_sub_aux {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (n : ℕ) : set_seq hf U_mem n ⊆ ↑(set_seq_aux hf U_mem n) :=
  set.bInter_subset_of_mem set.right_mem_Iic

theorem set_seq_prod_subset {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) {N : ℕ} {m : ℕ} {n : ℕ} (hm : N ≤ m) (hn : N ≤ n) : set.prod (set_seq hf U_mem m) (set_seq hf U_mem n) ⊆ U N := sorry

/-- A sequence of points such that `seq n ∈ set_seq n`. Here `set_seq` is a monotonically
decreasing sequence of sets `set_seq n ∈ f` with diameters controlled by a given sequence
of entourages. -/
def seq {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (n : ℕ) : α :=
  classical.some sorry

theorem seq_mem {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (n : ℕ) : seq hf U_mem n ∈ set_seq hf U_mem n :=
  classical.some_spec (filter.ne_bot.nonempty_of_mem (and.left hf) (set_seq_mem hf U_mem n))

theorem seq_pair_mem {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) {N : ℕ} {m : ℕ} {n : ℕ} (hm : N ≤ m) (hn : N ≤ n) : (seq hf U_mem m, seq hf U_mem n) ∈ U N :=
  set_seq_prod_subset hf U_mem hm hn { left := seq_mem hf U_mem m, right := seq_mem hf U_mem n }

theorem seq_is_cauchy_seq {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (U_le : ∀ (s : set (α × α)), s ∈ uniformity α → ∃ (n : ℕ), U n ⊆ s) : cauchy_seq (seq hf U_mem) :=
  cauchy_seq_of_controlled U U_le (seq_pair_mem hf U_mem)

/-- If the sequence `sequentially_complete.seq` converges to `a`, then `f ≤ 𝓝 a`. -/
theorem le_nhds_of_seq_tendsto_nhds {α : Type u} [uniform_space α] {f : filter α} (hf : cauchy f) {U : ℕ → set (α × α)} (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (U_le : ∀ (s : set (α × α)), s ∈ uniformity α → ∃ (n : ℕ), U n ⊆ s) {a : α} (ha : filter.tendsto (seq hf U_mem) filter.at_top (nhds a)) : f ≤ nhds a := sorry

end sequentially_complete

namespace uniform_space

/-- A uniform space is complete provided that (a) its uniformity filter has a countable basis;
(b) any sequence satisfying a "controlled" version of the Cauchy condition converges. -/
theorem complete_of_convergent_controlled_sequences {α : Type u} [uniform_space α] (H : filter.is_countably_generated (uniformity α)) (U : ℕ → set (α × α)) (U_mem : ∀ (n : ℕ), U n ∈ uniformity α) (HU : ∀ (u : ℕ → α), (∀ (N m n : ℕ), N ≤ m → N ≤ n → (u m, u n) ∈ U N) → ∃ (a : α), filter.tendsto u filter.at_top (nhds a)) : complete_space α := sorry

/-- A sequentially complete uniform space with a countable basis of the uniformity filter is
complete. -/
theorem complete_of_cauchy_seq_tendsto {α : Type u} [uniform_space α] (H : filter.is_countably_generated (uniformity α)) (H' : ∀ (u : ℕ → α), cauchy_seq u → ∃ (a : α), filter.tendsto u filter.at_top (nhds a)) : complete_space α := sorry

protected theorem first_countable_topology {α : Type u} [uniform_space α] (H : filter.is_countably_generated (uniformity α)) : topological_space.first_countable_topology α := sorry

/-- A separable uniform space with countably generated uniformity filter is second countable:
one obtains a countable basis by taking the balls centered at points in a dense subset,
and with rational "radii" from a countable open symmetric antimono basis of `𝓤 α`. We do not
register this as an instance, as there is already an instance going in the other direction
from second countable spaces to separable spaces, and we want to avoid loops. -/
theorem second_countable_of_separable {α : Type u} [uniform_space α] (H : filter.is_countably_generated (uniformity α)) [topological_space.separable_space α] : topological_space.second_countable_topology α := sorry


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

! This file was ported from Lean 3 source module topology.bornology.constructions
! leanprover-community/mathlib commit a11f9106a169dd302a285019e5165f8ab32ff433
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Bornology.Basic

/-!
# Bornology structure on products and subtypes

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

In this file we define `bornology` and `bounded_space` instances on `α × β`, `Π i, π i`, and
`{x // p x}`. We also prove basic lemmas about `bornology.cobounded` and `bornology.is_bounded`
on these types.
-/

open Set Filter Bornology Function

open Filter

variable {α β ι : Type _} {π : ι → Type _} [Fintype ι] [Bornology α] [Bornology β]
  [∀ i, Bornology (π i)]

instance : Bornology (α × β)
    where
  cobounded := (cobounded α).coprod (cobounded β)
  le_cofinite :=
    @coprod_cofinite α β ▸ coprod_mono ‹Bornology α›.le_cofinite ‹Bornology β›.le_cofinite

instance : Bornology (∀ i, π i)
    where
  cobounded := Filter.coprodᵢ fun i => cobounded (π i)
  le_cofinite := @coprodᵢ_cofinite ι π _ ▸ Filter.coprodᵢ_mono fun i => Bornology.le_cofinite _

#print Bornology.induced /-
/-- Inverse image of a bornology. -/
@[reducible]
def Bornology.induced {α β : Type _} [Bornology β] (f : α → β) : Bornology α
    where
  cobounded := comap f (cobounded β)
  le_cofinite := (comap_mono (Bornology.le_cofinite β)).trans (comap_cofinite_le _)
#align bornology.induced Bornology.induced
-/

instance {p : α → Prop} : Bornology (Subtype p) :=
  Bornology.induced (coe : Subtype p → α)

namespace Bornology

/-!
### Bounded sets in `α × β`
-/

/- warning: bornology.cobounded_prod -> Bornology.cobounded_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β], Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Bornology.cobounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3)) (Filter.coprod.{u1, u2} α β (Bornology.cobounded.{u1} α _inst_2) (Bornology.cobounded.{u2} β _inst_3))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_2 : Bornology.{u2} α] [_inst_3 : Bornology.{u1} β], Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Bornology.cobounded.{max u1 u2} (Prod.{u2, u1} α β) (instBornologyProd.{u2, u1} α β _inst_2 _inst_3)) (Filter.coprod.{u2, u1} α β (Bornology.cobounded.{u2} α _inst_2) (Bornology.cobounded.{u1} β _inst_3))
Case conversion may be inaccurate. Consider using '#align bornology.cobounded_prod Bornology.cobounded_prodₓ'. -/
theorem cobounded_prod : cobounded (α × β) = (cobounded α).coprod (cobounded β) :=
  rfl
#align bornology.cobounded_prod Bornology.cobounded_prod

/- warning: bornology.is_bounded_image_fst_and_snd -> Bornology.isBounded_image_fst_and_snd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{max u1 u2} (Prod.{u1, u2} α β)}, Iff (And (Bornology.IsBounded.{u1} α _inst_2 (Set.image.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) s)) (Bornology.IsBounded.{u2} β _inst_3 (Set.image.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) s))) (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3) s)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{max u2 u1} (Prod.{u1, u2} α β)}, Iff (And (Bornology.IsBounded.{u1} α _inst_2 (Set.image.{max u2 u1, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) s)) (Bornology.IsBounded.{u2} β _inst_3 (Set.image.{max u2 u1, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) s))) (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (instBornologyProd.{u1, u2} α β _inst_2 _inst_3) s)
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_image_fst_and_snd Bornology.isBounded_image_fst_and_sndₓ'. -/
theorem isBounded_image_fst_and_snd {s : Set (α × β)} :
    IsBounded (Prod.fst '' s) ∧ IsBounded (Prod.snd '' s) ↔ IsBounded s :=
  compl_mem_coprod.symm
#align bornology.is_bounded_image_fst_and_snd Bornology.isBounded_image_fst_and_snd

variable {s : Set α} {t : Set β} {S : ∀ i, Set (π i)}

/- warning: bornology.is_bounded.fst_of_prod -> Bornology.IsBounded.fst_of_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) -> (Set.Nonempty.{u2} β t) -> (Bornology.IsBounded.{u1} α _inst_2 s)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Bornology.IsBounded.{max u2 u1} (Prod.{u1, u2} α β) (instBornologyProd.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) -> (Set.Nonempty.{u2} β t) -> (Bornology.IsBounded.{u1} α _inst_2 s)
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded.fst_of_prod Bornology.IsBounded.fst_of_prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem IsBounded.fst_of_prod (h : IsBounded (s ×ˢ t)) (ht : t.Nonempty) : IsBounded s :=
  fst_image_prod s ht ▸ (isBounded_image_fst_and_snd.2 h).1
#align bornology.is_bounded.fst_of_prod Bornology.IsBounded.fst_of_prod

/- warning: bornology.is_bounded.snd_of_prod -> Bornology.IsBounded.snd_of_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) -> (Set.Nonempty.{u1} α s) -> (Bornology.IsBounded.{u2} β _inst_3 t)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Bornology.IsBounded.{max u2 u1} (Prod.{u1, u2} α β) (instBornologyProd.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) -> (Set.Nonempty.{u1} α s) -> (Bornology.IsBounded.{u2} β _inst_3 t)
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded.snd_of_prod Bornology.IsBounded.snd_of_prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem IsBounded.snd_of_prod (h : IsBounded (s ×ˢ t)) (hs : s.Nonempty) : IsBounded t :=
  snd_image_prod hs t ▸ (isBounded_image_fst_and_snd.2 h).2
#align bornology.is_bounded.snd_of_prod Bornology.IsBounded.snd_of_prod

/- warning: bornology.is_bounded.prod -> Bornology.IsBounded.prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Bornology.IsBounded.{u1} α _inst_2 s) -> (Bornology.IsBounded.{u2} β _inst_3 t) -> (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_2 : Bornology.{u2} α] [_inst_3 : Bornology.{u1} β] {s : Set.{u2} α} {t : Set.{u1} β}, (Bornology.IsBounded.{u2} α _inst_2 s) -> (Bornology.IsBounded.{u1} β _inst_3 t) -> (Bornology.IsBounded.{max u1 u2} (Prod.{u2, u1} α β) (instBornologyProd.{u2, u1} α β _inst_2 _inst_3) (Set.prod.{u2, u1} α β s t))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded.prod Bornology.IsBounded.prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem IsBounded.prod (hs : IsBounded s) (ht : IsBounded t) : IsBounded (s ×ˢ t) :=
  isBounded_image_fst_and_snd.1
    ⟨hs.Subset <| fst_image_prod_subset _ _, ht.Subset <| snd_image_prod_subset _ _⟩
#align bornology.is_bounded.prod Bornology.IsBounded.prod

/- warning: bornology.is_bounded_prod_of_nonempty -> Bornology.isBounded_prod_of_nonempty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Set.Nonempty.{max u1 u2} (Prod.{u1, u2} α β) (Set.prod.{u1, u2} α β s t)) -> (Iff (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) (And (Bornology.IsBounded.{u1} α _inst_2 s) (Bornology.IsBounded.{u2} β _inst_3 t)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, (Set.Nonempty.{max u2 u1} (Prod.{u1, u2} α β) (Set.prod.{u1, u2} α β s t)) -> (Iff (Bornology.IsBounded.{max u2 u1} (Prod.{u1, u2} α β) (instBornologyProd.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) (And (Bornology.IsBounded.{u1} α _inst_2 s) (Bornology.IsBounded.{u2} β _inst_3 t)))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_prod_of_nonempty Bornology.isBounded_prod_of_nonemptyₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem isBounded_prod_of_nonempty (hne : Set.Nonempty (s ×ˢ t)) :
    IsBounded (s ×ˢ t) ↔ IsBounded s ∧ IsBounded t :=
  ⟨fun h => ⟨h.fst_of_prod hne.snd, h.snd_of_prod hne.fst⟩, fun h => h.1.Prod h.2⟩
#align bornology.is_bounded_prod_of_nonempty Bornology.isBounded_prod_of_nonempty

/- warning: bornology.is_bounded_prod -> Bornology.isBounded_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, Iff (Bornology.IsBounded.{max u1 u2} (Prod.{u1, u2} α β) (Prod.bornology.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) (Or (Eq.{succ u1} (Set.{u1} α) s (EmptyCollection.emptyCollection.{u1} (Set.{u1} α) (Set.hasEmptyc.{u1} α))) (Or (Eq.{succ u2} (Set.{u2} β) t (EmptyCollection.emptyCollection.{u2} (Set.{u2} β) (Set.hasEmptyc.{u2} β))) (And (Bornology.IsBounded.{u1} α _inst_2 s) (Bornology.IsBounded.{u2} β _inst_3 t))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_2 : Bornology.{u1} α] [_inst_3 : Bornology.{u2} β] {s : Set.{u1} α} {t : Set.{u2} β}, Iff (Bornology.IsBounded.{max u2 u1} (Prod.{u1, u2} α β) (instBornologyProd.{u1, u2} α β _inst_2 _inst_3) (Set.prod.{u1, u2} α β s t)) (Or (Eq.{succ u1} (Set.{u1} α) s (EmptyCollection.emptyCollection.{u1} (Set.{u1} α) (Set.instEmptyCollectionSet.{u1} α))) (Or (Eq.{succ u2} (Set.{u2} β) t (EmptyCollection.emptyCollection.{u2} (Set.{u2} β) (Set.instEmptyCollectionSet.{u2} β))) (And (Bornology.IsBounded.{u1} α _inst_2 s) (Bornology.IsBounded.{u2} β _inst_3 t))))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_prod Bornology.isBounded_prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem isBounded_prod : IsBounded (s ×ˢ t) ↔ s = ∅ ∨ t = ∅ ∨ IsBounded s ∧ IsBounded t :=
  by
  rcases s.eq_empty_or_nonempty with (rfl | hs); · simp
  rcases t.eq_empty_or_nonempty with (rfl | ht); · simp
  simp only [hs.ne_empty, ht.ne_empty, is_bounded_prod_of_nonempty (hs.prod ht), false_or_iff]
#align bornology.is_bounded_prod Bornology.isBounded_prod

/- warning: bornology.is_bounded_prod_self -> Bornology.isBounded_prod_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : Bornology.{u1} α] {s : Set.{u1} α}, Iff (Bornology.IsBounded.{u1} (Prod.{u1, u1} α α) (Prod.bornology.{u1, u1} α α _inst_2 _inst_2) (Set.prod.{u1, u1} α α s s)) (Bornology.IsBounded.{u1} α _inst_2 s)
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : Bornology.{u1} α] {s : Set.{u1} α}, Iff (Bornology.IsBounded.{u1} (Prod.{u1, u1} α α) (instBornologyProd.{u1, u1} α α _inst_2 _inst_2) (Set.prod.{u1, u1} α α s s)) (Bornology.IsBounded.{u1} α _inst_2 s)
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_prod_self Bornology.isBounded_prod_selfₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem isBounded_prod_self : IsBounded (s ×ˢ s) ↔ IsBounded s :=
  by
  rcases s.eq_empty_or_nonempty with (rfl | hs); · simp
  exact (is_bounded_prod_of_nonempty (hs.prod hs)).trans (and_self_iff _)
#align bornology.is_bounded_prod_self Bornology.isBounded_prod_self

/-!
### Bounded sets in `Π i, π i`
-/

/- warning: bornology.cobounded_pi -> Bornology.cobounded_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)], Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), π i)) (Bornology.cobounded.{max u1 u2} (forall (i : ι), π i) (Pi.bornology.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i))) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => π i) (fun (i : ι) => Bornology.cobounded.{u2} (π i) (_inst_4 i)))
but is expected to have type
  forall {ι : Type.{u2}} {π : ι -> Type.{u1}} [_inst_1 : Fintype.{u2} ι] [_inst_4 : forall (i : ι), Bornology.{u1} (π i)], Eq.{max (succ u2) (succ u1)} (Filter.{max u2 u1} (forall (i : ι), π i)) (Bornology.cobounded.{max u2 u1} (forall (i : ι), π i) (instBornologyForAll.{u2, u1} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i))) (Filter.coprodᵢ.{u2, u1} ι (fun (i : ι) => π i) (fun (i : ι) => Bornology.cobounded.{u1} (π i) (_inst_4 i)))
Case conversion may be inaccurate. Consider using '#align bornology.cobounded_pi Bornology.cobounded_piₓ'. -/
theorem cobounded_pi : cobounded (∀ i, π i) = Filter.coprodᵢ fun i => cobounded (π i) :=
  rfl
#align bornology.cobounded_pi Bornology.cobounded_pi

/- warning: bornology.forall_is_bounded_image_eval_iff -> Bornology.forall_isBounded_image_eval_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)] {s : Set.{max u1 u2} (forall (i : ι), π i)}, Iff (forall (i : ι), Bornology.IsBounded.{u2} (π i) (_inst_4 i) (Set.image.{max u1 u2, u2} (forall (x : ι), π x) (π i) (Function.eval.{succ u1, succ u2} ι (fun (i : ι) => π i) i) s)) (Bornology.IsBounded.{max u1 u2} (forall (i : ι), π i) (Pi.bornology.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) s)
but is expected to have type
  forall {ι : Type.{u2}} {π : ι -> Type.{u1}} [_inst_1 : Fintype.{u2} ι] [_inst_4 : forall (i : ι), Bornology.{u1} (π i)] {s : Set.{max u2 u1} (forall (i : ι), π i)}, Iff (forall (i : ι), Bornology.IsBounded.{u1} (π i) (_inst_4 i) (Set.image.{max u2 u1, u1} (forall (x : ι), π x) (π i) (Function.eval.{succ u2, succ u1} ι (fun (i : ι) => π i) i) s)) (Bornology.IsBounded.{max u2 u1} (forall (i : ι), π i) (instBornologyForAll.{u2, u1} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) s)
Case conversion may be inaccurate. Consider using '#align bornology.forall_is_bounded_image_eval_iff Bornology.forall_isBounded_image_eval_iffₓ'. -/
theorem forall_isBounded_image_eval_iff {s : Set (∀ i, π i)} :
    (∀ i, IsBounded (eval i '' s)) ↔ IsBounded s :=
  compl_mem_coprodᵢ.symm
#align bornology.forall_is_bounded_image_eval_iff Bornology.forall_isBounded_image_eval_iff

/- warning: bornology.is_bounded.pi -> Bornology.IsBounded.pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)] {S : forall (i : ι), Set.{u2} (π i)}, (forall (i : ι), Bornology.IsBounded.{u2} (π i) (_inst_4 i) (S i)) -> (Bornology.IsBounded.{max u1 u2} (forall (i : ι), π i) (Pi.bornology.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) (Set.pi.{u1, u2} ι (fun (i : ι) => π i) (Set.univ.{u1} ι) S))
but is expected to have type
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)] {S : forall (i : ι), Set.{u2} (π i)}, (forall (i : ι), Bornology.IsBounded.{u2} (π i) (_inst_4 i) (S i)) -> (Bornology.IsBounded.{max u2 u1} (forall (i : ι), π i) (instBornologyForAll.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) (Set.pi.{u1, u2} ι (fun (i : ι) => π i) (Set.univ.{u1} ι) S))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded.pi Bornology.IsBounded.piₓ'. -/
theorem IsBounded.pi (h : ∀ i, IsBounded (S i)) : IsBounded (pi univ S) :=
  forall_isBounded_image_eval_iff.1 fun i => (h i).Subset eval_image_univ_pi_subset
#align bornology.is_bounded.pi Bornology.IsBounded.pi

/- warning: bornology.is_bounded_pi_of_nonempty -> Bornology.isBounded_pi_of_nonempty is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)] {S : forall (i : ι), Set.{u2} (π i)}, (Set.Nonempty.{max u1 u2} (forall (i : ι), π i) (Set.pi.{u1, u2} ι (fun (i : ι) => π i) (Set.univ.{u1} ι) S)) -> (Iff (Bornology.IsBounded.{max u1 u2} (forall (i : ι), π i) (Pi.bornology.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) (Set.pi.{u1, u2} ι (fun (i : ι) => π i) (Set.univ.{u1} ι) S)) (forall (i : ι), Bornology.IsBounded.{u2} (π i) (_inst_4 i) (S i)))
but is expected to have type
  forall {ι : Type.{u2}} {π : ι -> Type.{u1}} [_inst_1 : Fintype.{u2} ι] [_inst_4 : forall (i : ι), Bornology.{u1} (π i)] {S : forall (i : ι), Set.{u1} (π i)}, (Set.Nonempty.{max u2 u1} (forall (i : ι), π i) (Set.pi.{u2, u1} ι (fun (i : ι) => π i) (Set.univ.{u2} ι) S)) -> (Iff (Bornology.IsBounded.{max u1 u2} (forall (i : ι), π i) (instBornologyForAll.{u2, u1} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) (Set.pi.{u2, u1} ι (fun (i : ι) => π i) (Set.univ.{u2} ι) S)) (forall (i : ι), Bornology.IsBounded.{u1} (π i) (_inst_4 i) (S i)))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_pi_of_nonempty Bornology.isBounded_pi_of_nonemptyₓ'. -/
theorem isBounded_pi_of_nonempty (hne : (pi univ S).Nonempty) :
    IsBounded (pi univ S) ↔ ∀ i, IsBounded (S i) :=
  ⟨fun H i => @eval_image_univ_pi _ _ _ i hne ▸ forall_isBounded_image_eval_iff.2 H i, IsBounded.pi⟩
#align bornology.is_bounded_pi_of_nonempty Bornology.isBounded_pi_of_nonempty

/- warning: bornology.is_bounded_pi -> Bornology.isBounded_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)] {S : forall (i : ι), Set.{u2} (π i)}, Iff (Bornology.IsBounded.{max u1 u2} (forall (i : ι), π i) (Pi.bornology.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) (Set.pi.{u1, u2} ι (fun (i : ι) => π i) (Set.univ.{u1} ι) S)) (Or (Exists.{succ u1} ι (fun (i : ι) => Eq.{succ u2} (Set.{u2} (π i)) (S i) (EmptyCollection.emptyCollection.{u2} (Set.{u2} (π i)) (Set.hasEmptyc.{u2} (π i))))) (forall (i : ι), Bornology.IsBounded.{u2} (π i) (_inst_4 i) (S i)))
but is expected to have type
  forall {ι : Type.{u1}} {π : ι -> Type.{u2}} [_inst_1 : Fintype.{u1} ι] [_inst_4 : forall (i : ι), Bornology.{u2} (π i)] {S : forall (i : ι), Set.{u2} (π i)}, Iff (Bornology.IsBounded.{max u2 u1} (forall (i : ι), π i) (instBornologyForAll.{u1, u2} ι (fun (i : ι) => π i) _inst_1 (fun (i : ι) => _inst_4 i)) (Set.pi.{u1, u2} ι (fun (i : ι) => π i) (Set.univ.{u1} ι) S)) (Or (Exists.{succ u1} ι (fun (i : ι) => Eq.{succ u2} (Set.{u2} (π i)) (S i) (EmptyCollection.emptyCollection.{u2} (Set.{u2} (π i)) (Set.instEmptyCollectionSet.{u2} (π i))))) (forall (i : ι), Bornology.IsBounded.{u2} (π i) (_inst_4 i) (S i)))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_pi Bornology.isBounded_piₓ'. -/
theorem isBounded_pi : IsBounded (pi univ S) ↔ (∃ i, S i = ∅) ∨ ∀ i, IsBounded (S i) :=
  by
  by_cases hne : ∃ i, S i = ∅
  · simp [hne, univ_pi_eq_empty_iff.2 hne]
  · simp only [hne, false_or_iff]
    simp only [not_exists, ← Ne.def, ← nonempty_iff_ne_empty, ← univ_pi_nonempty_iff] at hne
    exact is_bounded_pi_of_nonempty hne
#align bornology.is_bounded_pi Bornology.isBounded_pi

/-!
### Bounded sets in `{x // p x}`
-/

/- warning: bornology.is_bounded_induced -> Bornology.isBounded_induced is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_5 : Bornology.{u2} β] {f : α -> β} {s : Set.{u1} α}, Iff (Bornology.IsBounded.{u1} α (Bornology.induced.{u1, u2} α β _inst_5 f) s) (Bornology.IsBounded.{u2} β _inst_5 (Set.image.{u1, u2} α β f s))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_5 : Bornology.{u1} β] {f : α -> β} {s : Set.{u2} α}, Iff (Bornology.IsBounded.{u2} α (Bornology.induced.{u2, u1} α β _inst_5 f) s) (Bornology.IsBounded.{u1} β _inst_5 (Set.image.{u2, u1} α β f s))
Case conversion may be inaccurate. Consider using '#align bornology.is_bounded_induced Bornology.isBounded_inducedₓ'. -/
theorem isBounded_induced {α β : Type _} [Bornology β] {f : α → β} {s : Set α} :
    @IsBounded α (Bornology.induced f) s ↔ IsBounded (f '' s) :=
  compl_mem_comap
#align bornology.is_bounded_induced Bornology.isBounded_induced

#print Bornology.isBounded_image_subtype_val /-
theorem isBounded_image_subtype_val {p : α → Prop} {s : Set { x // p x }} :
    IsBounded (coe '' s : Set α) ↔ IsBounded s :=
  isBounded_induced.symm
#align bornology.is_bounded_image_subtype_coe Bornology.isBounded_image_subtype_val
-/

end Bornology

/-!
### Bounded spaces
-/

open Bornology

instance [BoundedSpace α] [BoundedSpace β] : BoundedSpace (α × β) := by
  simp [← cobounded_eq_bot_iff, cobounded_prod]

instance [∀ i, BoundedSpace (π i)] : BoundedSpace (∀ i, π i) := by
  simp [← cobounded_eq_bot_iff, cobounded_pi]

/- warning: bounded_space_induced_iff -> boundedSpace_induced_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_5 : Bornology.{u2} β] {f : α -> β}, Iff (BoundedSpace.{u1} α (Bornology.induced.{u1, u2} α β _inst_5 f)) (Bornology.IsBounded.{u2} β _inst_5 (Set.range.{u2, succ u1} β α f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_5 : Bornology.{u1} β] {f : α -> β}, Iff (BoundedSpace.{u2} α (Bornology.induced.{u2, u1} α β _inst_5 f)) (Bornology.IsBounded.{u1} β _inst_5 (Set.range.{u1, succ u2} β α f))
Case conversion may be inaccurate. Consider using '#align bounded_space_induced_iff boundedSpace_induced_iffₓ'. -/
theorem boundedSpace_induced_iff {α β : Type _} [Bornology β] {f : α → β} :
    @BoundedSpace α (Bornology.induced f) ↔ IsBounded (range f) := by
  rw [← is_bounded_univ, is_bounded_induced, image_univ]
#align bounded_space_induced_iff boundedSpace_induced_iff

#print boundedSpace_subtype_iff /-
theorem boundedSpace_subtype_iff {p : α → Prop} :
    BoundedSpace (Subtype p) ↔ IsBounded { x | p x } := by
  rw [boundedSpace_induced_iff, Subtype.range_coe_subtype]
#align bounded_space_subtype_iff boundedSpace_subtype_iff
-/

#print boundedSpace_val_set_iff /-
theorem boundedSpace_val_set_iff {s : Set α} : BoundedSpace s ↔ IsBounded s :=
  boundedSpace_subtype_iff
#align bounded_space_coe_set_iff boundedSpace_val_set_iff
-/

alias boundedSpace_subtype_iff ↔ _ Bornology.IsBounded.boundedSpace_subtype
#align bornology.is_bounded.bounded_space_subtype Bornology.IsBounded.boundedSpace_subtype

alias boundedSpace_val_set_iff ↔ _ Bornology.IsBounded.boundedSpace_val
#align bornology.is_bounded.bounded_space_coe Bornology.IsBounded.boundedSpace_val

instance [BoundedSpace α] {p : α → Prop} : BoundedSpace (Subtype p) :=
  (IsBounded.all { x | p x }).boundedSpace_subtype

/-!
### `additive`, `multiplicative`

The bornology on those type synonyms is inherited without change.
-/

instance : Bornology (Additive α) :=
  ‹Bornology α›

instance : Bornology (Multiplicative α) :=
  ‹Bornology α›

instance [BoundedSpace α] : BoundedSpace (Additive α) :=
  ‹BoundedSpace α›

instance [BoundedSpace α] : BoundedSpace (Multiplicative α) :=
  ‹BoundedSpace α›

/-!
### Order dual

The bornology on this type synonym is inherited without change.
-/

instance : Bornology αᵒᵈ :=
  ‹Bornology α›

instance [BoundedSpace α] : BoundedSpace αᵒᵈ :=
  ‹BoundedSpace α›


import GroundZero.Cubical.V
open GroundZero.Cubical

/-
  This example on Lean.
  https://tonpa.guru/stream/2017/2017-10-31%20Доказательство%20коммутативности%20в%20кубике.txt
-/

namespace GroundZero.Cubical.Cubicaltt

hott def add (m : ℕ) : ℕ → ℕ
| Nat.zero   => m
| Nat.succ n => Nat.succ (add m n)

hott def addZero : Π a, Path ℕ (add Nat.zero a) a
| Nat.zero   => <_> Nat.zero
| Nat.succ a => <i> Nat.succ ((addZero a) @ i)

hott def addSucc (a : ℕ) : Π b, Path ℕ (add (Nat.succ a) b) (Nat.succ (add a b))
| Nat.zero   => <_> Nat.succ a
| Nat.succ b => <i> Nat.succ ((addSucc a b) @ i)

hott def addZeroInv : Π a, Path ℕ a (add a Nat.zero) :=
Path.refl

hott def addComm (a : ℕ) : Π b, Path ℕ (add a b) (add b a)
| Nat.zero   => <i> (addZero a) @ −i
| Nat.succ b => Path.kan (<i> Nat.succ ((addComm a b) @ i)) (<j> Nat.succ (add a b)) (<j> (addSucc b a) @ −j)

hott def addAssoc (a b : ℕ) : Π c, Path ℕ (add a (add b c)) (add (add a b) c)
| Nat.zero   => <_> add a b
| Nat.succ c => <i> Nat.succ ((addAssoc a b c) @ i)

hott def addComm₃ {a b c : ℕ} : Path ℕ (add a (add b c)) (add c (add b a) ):=
let r : Path ℕ (add a (add b c)) (add a (add c b)) :=
<i> add a ((addComm b c) @ i);
Path.kan (addComm a (add c b)) (<j> r @ −j) (<j> (addAssoc c b a) @ −j)

example (n m : ℕ) (h : Path ℕ n m) : Path ℕ (n + 1) (m + 1) :=
<i> Nat.succ (h @ i)

end GroundZero.Cubical.Cubicaltt


/-
This is a d∃∀duction file providing easy and progressive exercises for basic set theory.
It may be used as a tutorial for d∃∀duction.
French version.
-/

import data.set
import tactic

-- dEAduction tactics
import structures2      -- hypo_analysis, targets_analysis
import utils            -- no_meta_vars
import user_notations   -- notations that can be used in deaduction UI for a new object
import push_neg_once

-- dEAduction definitions
import set_definitions

-------------------------
-- dEAduction METADATA --
-------------------------

/- dEAduction
Title
    Tutoriel
Author
    Camille Lichère
Institution
    Université de France
Description
    Ce fichier contient quelques exercices faciles et progressifs de théorie élémentaire
    des ensembles. Il peut être utilisé comme tutoriel pour d∃∀duction ; en particulier,
    les boutons logiques sont introduits progressivement.
AvailableProof
    ALL -new_object
AvailableMagic
    assumption
-/

local attribute [instance] classical.prop_decidable

---------------------------------------------
-- global parameters = implicit variables --
---------------------------------------------
section course
parameters {X Y Z: Type}

open set

------------------------
-- COURSE DEFINITIONS --
------------------------
namespace definitions
/- dEAduction
PrettyName
    Définitions
-/

namespace inclusions_egalites
/- dEAduction
PrettyName
    Inclusions, égalités
-/

lemma definition.inclusion {A B : set X} : A ⊆ B ↔ ∀ {x:X}, x ∈ A → x ∈ B :=
/- dEAduction
ImplicitUse
    True
-/
begin
    exact iff.rfl,
end

lemma definition.egalite_deux_ensembles {A A' : set X} :
(A = A') ↔ ( ∀ x, x ∈ A ↔ x ∈ A' ) :=
/- dEAduction
PrettyName
    Egalité de deux ensembles
ImplicitUse
    False
-/
begin
     exact set.ext_iff,
end

lemma definition.double_inclusion (A A' : set X) :
A = A' ↔ (A ⊆ A' ∧ A' ⊆ A):=
/- dEAduction
PrettyName
    Double inclusion
ImplicitUse
    True
-/
begin
    exact subset.antisymm_iff,
end
end inclusions_egalites

namespace unions_intersections
/- dEAduction
PrettyName
    Unions, intersections
-/
lemma definition.intersection_deux_ensembles {A B : set X} {x : X} :
x ∈ A ∩ B ↔ ( x ∈ A ∧ x ∈ B) :=
/- dEAduction
PrettyName
    Intersection de deux ensembles
ImplicitUse
    True
-/
begin
    exact iff.rfl,
end

lemma definition.union_deux_ensembles  {A : set X} {B : set X} {x : X} :
x ∈ A ∪ B ↔ ( x ∈ A ∨ x ∈ B) :=
/- dEAduction
PrettyName
    Union de deux ensembles
ImplicitUse
    True
-/
begin
    exact iff.rfl,
end

end unions_intersections

end definitions
---------------
-- SECTION 1 --
---------------
-- variables exercices --
-- variables {A B C : set X}

---------------
-- EXERCICES --
---------------
namespace exercices

lemma exercise.intersection_inclus_ensemble
(A B : set X) :
A ∩ B ⊆ A
:=
/- dEAduction
PrettyName
    Un ensemble contient son intersection avec un autre
Description
    Voici un premier exercice !
-/
begin
    todo,
end

lemma exercise.inclus_dans_les_deux_implique_dans_lintersection
(A B C : set X) :
(C ⊆ A) ∧ (C ⊆ B) → C ⊆ A ∩ B
:=
/- dEAduction
PrettyName
    Inclus dans les deux implique inclus dans l'intersection
Description
    Voici un deuxième exercice !
-/
begin
    todo,
end

lemma exercise.inclusion_transitive
(A B C : set X) :
(A ⊆ B ∧ B ⊆ C) → A ⊆ C
:=
/- dEAduction
PrettyName
    Transitivité de l'inclusion
Description
    Voici un troisième exercice !
-/
begin
    todo,
end

lemma exercise.ensemble_inclus_union
(A B : set X) :
B ⊆ A ∪ B
:=
/- dEAduction
PrettyName
    Ensemble inclus dans l'union
Description
    Le bouton ∨ ("ou"), permet notamment de montrer un but de la forme "P ou Q" en choisissant si on veut montrer "P" ou "Q".
-/
begin
    todo,
end

lemma exercise.ensemble_inclus_intersection
(A B : set X) :
A ⊆ A ∩ B  → (A ∪ B) = B
:=
/- dEAduction
PrettyName
    Ensemble inclus dans l'intersection
Description
    Utilisez la double inclusion pour montrer une égalité entre ensembles.
    Le bouton ∨ ("ou") permet également, appliqué à une hypothèse du type "P ou Q" de faire une disjonction de cas selon si on a "P" ou "Q".
-/
begin
    todo,
end

lemma exercise.inter_distributive_union
(A B C : set X):
A ∪ (B ∩ C) = (A ∪ B) ∩ (A ∪ C)
:=
/- dEAduction
PrettyName
    Union avec une intersection
Description
    Utilisez l'aperçu de preuve pour ne pas vous perdre dans les différents cas.
-/
begin
    todo,
end

lemma exercise.exercice_bilan
(A B : set X) :
A ⊆ B ↔ A ∩ B = A
:=
/- dEAduction
PrettyName
    Exercice bilan
Description
    On peut utiliser une égalité pour remplacer l'un des termes par l'autre.
-/
begin
    todo
end
end exercices

end course


import tactic

/--------------------------------------------------------------------------

You can prove exactly one of the following three using just
``refine``, ``rintro``, and ``have``.

Can you find which one?

--------------------------------------------------------------------------/

theorem not_not_self_imp_self (P : Prop) : ¬ ¬ P → P:=
begin
  sorry,
end

theorem contrapositive_converse (P Q : Prop) : (¬Q → ¬P) → (P → Q) :=
begin
  sorry,
end

example (P : Prop) : ¬ P → ¬ ¬ ¬ P :=
begin
  sorry,
end


lemma pow_pow (a m n : mynat) : (a ^ m) ^ n = a ^ (m * n) :=
begin
induction n with h hd,
rw mul_zero,
repeat {rw pow_zero},
rw pow_succ,
rw hd,
rw mul_succ,
rwa pow_add,
end


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.preadditive.projective_resolution

/-!
# Left-derived functors

We define the left-derived functors `F.left_derived n : C ⥤ D` for any additive functor `F`
out of a category with projective resolutions.

The definition is
```
projective_resolutions C ⋙ F.map_homotopy_category _ ⋙ homotopy_category.homology_functor D _ n
```
that is, we pick a projective resolution (thought of as an object of the homotopy category),
we apply `F` objectwise, and compute `n`-th homology.

We show that these left-derived functors can be calculated
on objects using any choice of projective resolution,
and on morphisms by any choice of lift to a chain map between chosen projective resolutions.

Similarly we define natural transformations between left-derived functors coming from
natural transformations between the original additive functors,
and show how to compute the components.

## Implementation

We don't assume the categories involved are abelian
(just preadditive, and have equalizers, cokernels, and image maps),
or that the functors are right exact.
None of these assumptions are needed yet.

It is often convenient, of course, to work with `[abelian C] [enough_projectives C] [abelian D]`
which (assuming the results from `category_theory.abelian.projective`) are enough to
provide all the typeclass hypotheses assumed here.
-/

noncomputable theory

open category_theory
open category_theory.limits

universes v u

namespace category_theory
variables {C : Type u} [category.{v} C] {D : Type*} [category D]

-- Importing `category_theory.abelian.projective` and assuming
-- `[abelian C] [enough_projectives C] [abelian D]` suffices to acquire all the following:
variables [preadditive C] [has_zero_object C] [has_equalizers C]
  [has_images C] [has_projective_resolutions C]
variables [preadditive D] [has_equalizers D] [has_cokernels D]
  [has_images D] [has_image_maps D]

/-- The left derived functors of an additive functor. -/
def functor.left_derived (F : C ⥤ D) [F.additive] (n : ℕ) : C ⥤ D :=
projective_resolutions C ⋙ F.map_homotopy_category _ ⋙ homotopy_category.homology_functor D _ n

-- TODO the left derived functors are additive (and linear when `F` is linear)

/-- We can compute a left derived functor using a chosen projective resolution. -/
@[simps]
def functor.left_derived_obj_iso (F : C ⥤ D) [F.additive] (n : ℕ)
  {X : C} (P : ProjectiveResolution X) :
  (F.left_derived n).obj X ≅
    (homology_functor D _ n).obj ((F.map_homological_complex _).obj P.complex) :=
(homotopy_category.homology_functor D _ n).map_iso
  (homotopy_category.iso_of_homotopy_equiv
    (F.map_homotopy_equiv (ProjectiveResolution.homotopy_equiv _ P)))
  ≪≫ (homotopy_category.homology_factors D _ n).app _

section
variables [has_zero_object D]

/-- The 0-th derived functor of `F` on a projective object `X` is just `F.obj X`. -/
@[simps]
def functor.left_derived_obj_projective_zero (F : C ⥤ D) [F.additive]
  (X : C) [projective X] :
  (F.left_derived 0).obj X ≅ F.obj X :=
F.left_derived_obj_iso 0 (ProjectiveResolution.self X) ≪≫
  (homology_functor _ _ _).map_iso ((chain_complex.single₀_map_homological_complex F).app X) ≪≫
  (chain_complex.homology_functor_0_single₀ D).app (F.obj X)

open_locale zero_object

/-- The higher derived functors vanish on projective objects. -/
@[simps inv]
def functor.left_derived_obj_projective_succ (F : C ⥤ D) [F.additive] (n : ℕ)
  (X : C) [projective X] :
  (F.left_derived (n+1)).obj X ≅ 0 :=
F.left_derived_obj_iso (n+1) (ProjectiveResolution.self X) ≪≫
  (homology_functor _ _ _).map_iso ((chain_complex.single₀_map_homological_complex F).app X) ≪≫
  (chain_complex.homology_functor_succ_single₀ D n).app (F.obj X) ≪≫
  (functor.zero_obj _).iso_zero

end

/--
We can compute a left derived functor on a morphism using a lift of that morphism
to a chain map between chosen projective resolutions.
-/
lemma functor.left_derived_map_eq (F : C ⥤ D) [F.additive] (n : ℕ) {X Y : C} (f : X ⟶ Y)
  {P : ProjectiveResolution X} {Q : ProjectiveResolution Y} (g : P.complex ⟶ Q.complex)
  (w : g ≫ Q.π = P.π ≫ (chain_complex.single₀ C).map f) :
  (F.left_derived n).map f =
  (F.left_derived_obj_iso n P).hom ≫
    (homology_functor D _ n).map ((F.map_homological_complex _).map g) ≫
    (F.left_derived_obj_iso n Q).inv :=
begin
  dsimp only [functor.left_derived, functor.left_derived_obj_iso],
  dsimp, simp only [category.comp_id, category.id_comp],
  rw [←homology_functor_map, homotopy_category.homology_functor_map_factors],
  simp only [←functor.map_comp],
  congr' 1,
  apply homotopy_category.eq_of_homotopy,
  apply functor.map_homotopy,
  apply homotopy.trans,
  exact homotopy_category.homotopy_out_map _,
  apply ProjectiveResolution.lift_homotopy f,
  { simp, },
  { simp [w], },
end

/-- The natural transformation between left-derived functors induced by a natural transformation. -/
@[simps]
def nat_trans.left_derived {F G : C ⥤ D} [F.additive] [G.additive] (α : F ⟶ G) (n : ℕ) :
  F.left_derived n ⟶ G.left_derived n :=
whisker_left (projective_resolutions C)
  (whisker_right (nat_trans.map_homotopy_category α _)
    (homotopy_category.homology_functor D _ n))

@[simp] lemma nat_trans.left_derived_id (F : C ⥤ D) [F.additive] (n : ℕ) :
  nat_trans.left_derived (𝟙 F) n = 𝟙 (F.left_derived n) :=
by { simp [nat_trans.left_derived], refl, }

-- The `simp_nf` linter times out here, so we disable it.
@[simp, nolint simp_nf] lemma nat_trans.left_derived_comp
  {F G H : C ⥤ D} [F.additive] [G.additive] [H.additive]
  (α : F ⟶ G) (β : G ⟶ H) (n : ℕ) :
  nat_trans.left_derived (α ≫ β) n = nat_trans.left_derived α n ≫ nat_trans.left_derived β n :=
by simp [nat_trans.left_derived]

/--
A component of the natural transformation between left-derived functors can be computed
using a chosen projective resolution.
-/
lemma nat_trans.left_derived_eq {F G : C ⥤ D} [F.additive] [G.additive] (α : F ⟶ G) (n : ℕ)
  {X : C} (P : ProjectiveResolution X) :
  (nat_trans.left_derived α n).app X =
    (F.left_derived_obj_iso n P).hom ≫
      (homology_functor D _ n).map ((nat_trans.map_homological_complex α _).app P.complex) ≫
        (G.left_derived_obj_iso n P).inv :=
begin
  symmetry,
  dsimp [nat_trans.left_derived, functor.left_derived_obj_iso],
  simp only [category.comp_id, category.id_comp],
  rw [←homology_functor_map, homotopy_category.homology_functor_map_factors],
  simp only [←functor.map_comp],
  congr' 1,
  apply homotopy_category.eq_of_homotopy,
  simp only [nat_trans.map_homological_complex_naturality_assoc,
    ←functor.map_comp],
  apply homotopy.comp_left_id,
  rw [←functor.map_id],
  apply functor.map_homotopy,
  apply homotopy_equiv.homotopy_hom_inv_id,
end

-- TODO:
-- lemma nat_trans.left_derived_projective_zero {F G : C ⥤ D} [F.additive] [G.additive] (α : F ⟶ G)
--   (X : C) [projective X] :
--   (nat_trans.left_derived α 0).app X =
--     (F.left_derived_obj_projective_zero X).hom ≫
--       α.app X ≫
--         (G.left_derived_obj_projective_zero X).inv := sorry

-- TODO:
-- lemma nat_trans.left_derived_projective_succ {F G : C ⥤ D} [F.additive] [G.additive] (α : F ⟶ G)
--   (n : ℕ) (X : C) [projective X] :
--   (nat_trans.left_derived α (n+1)).app X = 0 := sorry

-- TODO left-derived functors of the identity functor are the identity
-- (requires we assume `abelian`?)
-- PROJECT left-derived functors of a composition (Grothendieck sequence)

end category_theory