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

! This file was ported from Lean 3 source module ring_theory.norm
! leanprover-community/mathlib commit 5fe298160aa02b0f3cf95690a1265232cdd9563c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.FieldTheory.PrimitiveElement
import Mathbin.LinearAlgebra.Determinant
import Mathbin.LinearAlgebra.FiniteDimensional
import Mathbin.LinearAlgebra.Matrix.Charpoly.Minpoly
import Mathbin.LinearAlgebra.Matrix.ToLinearEquiv
import Mathbin.FieldTheory.IsAlgClosed.AlgebraicClosure
import Mathbin.FieldTheory.Galois

/-!
# Norm for (finite) ring extensions

Suppose we have an `R`-algebra `S` with a finite basis. For each `s : S`,
the determinant of the linear map given by multiplying by `s` gives information
about the roots of the minimal polynomial of `s` over `R`.

## Implementation notes

Typically, the norm is defined specifically for finite field extensions.
The current definition is as general as possible and the assumption that we have
fields or that the extension is finite is added to the lemmas as needed.

We only define the norm for left multiplication (`algebra.left_mul_matrix`,
i.e. `linear_map.mul_left`).
For now, the definitions assume `S` is commutative, so the choice doesn't
matter anyway.

See also `algebra.trace`, which is defined similarly as the trace of
`algebra.left_mul_matrix`.

## References

 * https://en.wikipedia.org/wiki/Field_norm

-/

universe u v w

variable {R S T : Type _} [CommRing R] [Ring S]

variable [Algebra R S]

variable {K L F : Type _} [Field K] [Field L] [Field F]

variable [Algebra K L] [Algebra K F]

variable {ι : Type w}

open FiniteDimensional

open LinearMap

open Matrix Polynomial

open BigOperators

open Matrix

namespace Algebra

variable (R)

/-- The norm of an element `s` of an `R`-algebra is the determinant of `(*) s`. -/
noncomputable def norm : S →* R :=
  LinearMap.det.comp (LinearMap.Algebra.lmul R S).toRingHom.toMonoidHom
#align algebra.norm Algebra.norm

theorem norm_apply (x : S) : norm R x = LinearMap.det (LinearMap.Algebra.lmul R S x) :=
  rfl
#align algebra.norm_apply Algebra.norm_apply

theorem norm_eq_one_of_not_exists_basis (h : ¬∃ s : Finset S, Nonempty (Basis s R S)) (x : S) :
    norm R x = 1 := by
  rw [norm_apply, LinearMap.det]
  split_ifs with h
  rfl
#align algebra.norm_eq_one_of_not_exists_basis Algebra.norm_eq_one_of_not_exists_basis

variable {R}

-- Can't be a `simp` lemma because it depends on a choice of basis
theorem norm_eq_matrix_det [Fintype ι] [DecidableEq ι] (b : Basis ι R S) (s : S) :
    norm R s = Matrix.det (Algebra.leftMulMatrix b s) :=
  by
  rwa [norm_apply, ← LinearMap.det_toMatrix b, ← to_matrix_lmul_eq]
  rfl
#align algebra.norm_eq_matrix_det Algebra.norm_eq_matrix_det

/-- If `x` is in the base ring `K`, then the norm is `x ^ [L : K]`. -/
theorem norm_algebraMap_of_basis [Fintype ι] (b : Basis ι R S) (x : R) :
    norm R (algebraMap R S x) = x ^ Fintype.card ι :=
  by
  haveI := Classical.decEq ι
  rw [norm_apply, ← det_to_matrix b, lmul_algebra_map]
  convert@det_diagonal _ _ _ _ _ fun i : ι => x
  · ext (i j)
    rw [to_matrix_lsmul, Matrix.diagonal]
  · rw [Finset.prod_const, Finset.card_univ]
#align algebra.norm_algebra_map_of_basis Algebra.norm_algebraMap_of_basis

/-- If `x` is in the base field `K`, then the norm is `x ^ [L : K]`.

(If `L` is not finite-dimensional over `K`, then `norm = 1 = x ^ 0 = x ^ (finrank L K)`.)
-/
@[simp]
protected theorem norm_algebraMap {L : Type _} [Ring L] [Algebra K L] (x : K) :
    norm K (algebraMap K L x) = x ^ finrank K L :=
  by
  by_cases H : ∃ s : Finset L, Nonempty (Basis s K L)
  · rw [norm_algebra_map_of_basis H.some_spec.some, finrank_eq_card_basis H.some_spec.some]
  · rw [norm_eq_one_of_not_exists_basis K H, finrank_eq_zero_of_not_exists_basis, pow_zero]
    rintro ⟨s, ⟨b⟩⟩
    exact H ⟨s, ⟨b⟩⟩
#align algebra.norm_algebra_map Algebra.norm_algebraMap

section EqProdRoots

/-- Given `pb : power_basis K S`, then the norm of `pb.gen` is
`(-1) ^ pb.dim * coeff (minpoly K pb.gen) 0`. -/
theorem PowerBasis.norm_gen_eq_coeff_zero_minpoly (pb : PowerBasis R S) :
    norm R pb.gen = (-1) ^ pb.dim * coeff (minpoly R pb.gen) 0 := by
  rw [norm_eq_matrix_det pb.basis, det_eq_sign_charpoly_coeff, charpoly_leftMulMatrix,
    Fintype.card_fin]
#align algebra.power_basis.norm_gen_eq_coeff_zero_minpoly Algebra.PowerBasis.norm_gen_eq_coeff_zero_minpoly

/-- Given `pb : power_basis R S`, then the norm of `pb.gen` is
`((minpoly R pb.gen).map (algebra_map R F)).roots.prod`. -/
theorem PowerBasis.norm_gen_eq_prod_roots [Algebra R F] (pb : PowerBasis R S)
    (hf : (minpoly R pb.gen).Splits (algebraMap R F)) :
    algebraMap R F (norm R pb.gen) = ((minpoly R pb.gen).map (algebraMap R F)).roots.Prod :=
  by
  haveI := Module.nontrivial R F
  have := minpoly.monic pb.is_integral_gen
  rw [power_basis.norm_gen_eq_coeff_zero_minpoly, ← pb.nat_degree_minpoly, RingHom.map_mul, ←
    coeff_map,
    prod_roots_eq_coeff_zero_of_monic_of_split (this.map _) ((splits_id_iff_splits _).2 hf),
    this.nat_degree_map, map_pow, ← mul_assoc, ← mul_pow]
  · simp only [map_neg, _root_.map_one, neg_mul, neg_neg, one_pow, one_mul]; infer_instance
#align algebra.power_basis.norm_gen_eq_prod_roots Algebra.PowerBasis.norm_gen_eq_prod_roots

end EqProdRoots

section EqZeroIff

variable [Finite ι]

@[simp]
theorem norm_zero [Nontrivial S] [Module.Free R S] [Module.Finite R S] : norm R (0 : S) = 0 :=
  by
  nontriviality
  rw [norm_apply, coe_lmul_eq_mul, map_zero, LinearMap.det_zero' (Module.Free.chooseBasis R S)]
#align algebra.norm_zero Algebra.norm_zero

@[simp]
theorem norm_eq_zero_iff [IsDomain R] [IsDomain S] [Module.Free R S] [Module.Finite R S] {x : S} :
    norm R x = 0 ↔ x = 0 := by
  constructor
  let b := Module.Free.chooseBasis R S
  swap;
  · rintro rfl
    exact norm_zero
  · letI := Classical.decEq (Module.Free.ChooseBasisIndex R S)
    rw [norm_eq_matrix_det b, ← Matrix.exists_mulVec_eq_zero_iff]
    rintro ⟨v, v_ne, hv⟩
    rw [← b.equiv_fun.apply_symm_apply v, b.equiv_fun_symm_apply, b.equiv_fun_apply,
      left_mul_matrix_mul_vec_repr] at hv
    refine'
      (mul_eq_zero.mp (b.ext_elem fun i => _)).resolve_right (show (∑ i, v i • b i) ≠ 0 from _)
    · simpa only [LinearEquiv.map_zero, Pi.zero_apply] using congr_fun hv i
    · contrapose! v_ne with sum_eq
      apply b.equiv_fun.symm.injective
      rw [b.equiv_fun_symm_apply, sum_eq, LinearEquiv.map_zero]
#align algebra.norm_eq_zero_iff Algebra.norm_eq_zero_iff

theorem norm_ne_zero_iff [IsDomain R] [IsDomain S] [Module.Free R S] [Module.Finite R S] {x : S} :
    norm R x ≠ 0 ↔ x ≠ 0 :=
  not_iff_not.mpr norm_eq_zero_iff
#align algebra.norm_ne_zero_iff Algebra.norm_ne_zero_iff

/-- This is `algebra.norm_eq_zero_iff` composed with `algebra.norm_apply`. -/
@[simp]
theorem norm_eq_zero_iff' [IsDomain R] [IsDomain S] [Module.Free R S] [Module.Finite R S] {x : S} :
    LinearMap.det (LinearMap.mul R S x) = 0 ↔ x = 0 :=
  norm_eq_zero_iff
#align algebra.norm_eq_zero_iff' Algebra.norm_eq_zero_iff'

theorem norm_eq_zero_iff_of_basis [IsDomain R] [IsDomain S] (b : Basis ι R S) {x : S} :
    Algebra.norm R x = 0 ↔ x = 0 :=
  by
  haveI : Module.Free R S := Module.Free.of_basis b
  haveI : Module.Finite R S := Module.Finite.of_basis b
  exact norm_eq_zero_iff
#align algebra.norm_eq_zero_iff_of_basis Algebra.norm_eq_zero_iff_of_basis

theorem norm_ne_zero_iff_of_basis [IsDomain R] [IsDomain S] (b : Basis ι R S) {x : S} :
    Algebra.norm R x ≠ 0 ↔ x ≠ 0 :=
  not_iff_not.mpr (norm_eq_zero_iff_of_basis b)
#align algebra.norm_ne_zero_iff_of_basis Algebra.norm_ne_zero_iff_of_basis

end EqZeroIff

open IntermediateField

variable (K)

/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
theorem norm_eq_norm_adjoin [FiniteDimensional K L] [IsSeparable K L] (x : L) :
    norm K x = norm K (AdjoinSimple.gen K x) ^ finrank K⟮⟯ L :=
  by
  letI := isSeparable_tower_top_of_isSeparable K K⟮⟯ L
  let pbL := Field.powerBasisOfFiniteOfSeparable K⟮⟯ L
  let pbx := IntermediateField.adjoin.powerBasis (IsSeparable.isIntegral K x)
  rw [← adjoin_simple.algebra_map_gen K x, norm_eq_matrix_det (pbx.basis.smul pbL.basis) _,
    smul_left_mul_matrix_algebra_map, det_block_diagonal, norm_eq_matrix_det pbx.basis]
  simp only [Finset.card_fin, Finset.prod_const]
  congr
  rw [← PowerBasis.finrank, adjoin_simple.algebra_map_gen K x]
#align algebra.norm_eq_norm_adjoin Algebra.norm_eq_norm_adjoin

variable {K}

section IntermediateField

/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
theorem IntermediateField.AdjoinSimple.norm_gen_eq_one {x : L} (hx : ¬IsIntegral K x) :
    norm K (AdjoinSimple.gen K x) = 1 :=
  by
  rw [norm_eq_one_of_not_exists_basis]
  contrapose! hx
  obtain ⟨s, ⟨b⟩⟩ := hx
  refine' isIntegral_of_mem_of_fg K⟮⟯.toSubalgebra _ x _
  · exact (Submodule.fg_iff_finiteDimensional _).mpr (of_fintype_basis b)
  · exact IntermediateField.subset_adjoin K _ (Set.mem_singleton x)
#align intermediate_field.adjoin_simple.norm_gen_eq_one IntermediateField.AdjoinSimple.norm_gen_eq_one

/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
theorem IntermediateField.AdjoinSimple.norm_gen_eq_prod_roots (x : L)
    (hf : (minpoly K x).Splits (algebraMap K F)) :
    (algebraMap K F) (norm K (AdjoinSimple.gen K x)) =
      ((minpoly K x).map (algebraMap K F)).roots.Prod :=
  by
  have injKxL := (algebraMap K⟮⟯ L).Injective
  by_cases hx : IsIntegral K x
  swap
  · simp [minpoly.eq_zero hx, IntermediateField.AdjoinSimple.norm_gen_eq_one hx]
  have hx' : IsIntegral K (adjoin_simple.gen K x) :=
    by
    rwa [← isIntegral_algebraMap_iff injKxL, adjoin_simple.algebra_map_gen]
    infer_instance
  rw [← adjoin.power_basis_gen hx, power_basis.norm_gen_eq_prod_roots] <;>
      rw [adjoin.power_basis_gen hx, minpoly.eq_of_algebraMap_eq injKxL hx'] <;>
    try simp only [adjoin_simple.algebra_map_gen _ _]
  exact hf
#align intermediate_field.adjoin_simple.norm_gen_eq_prod_roots IntermediateField.AdjoinSimple.norm_gen_eq_prod_roots

end IntermediateField

section EqProdEmbeddings

open IntermediateField IntermediateField.AdjoinSimple Polynomial

variable (F) (E : Type _) [Field E] [Algebra K E]

theorem norm_eq_prod_embeddings_gen [Algebra R F] (pb : PowerBasis R S)
    (hE : (minpoly R pb.gen).Splits (algebraMap R F)) (hfx : (minpoly R pb.gen).Separable) :
    algebraMap R F (norm R pb.gen) = (@Finset.univ pb.AlgHom.Fintype).Prod fun σ => σ pb.gen :=
  by
  letI := Classical.decEq F
  rw [pb.norm_gen_eq_prod_roots hE, Fintype.prod_equiv pb.lift_equiv', Finset.prod_mem_multiset,
    Finset.prod_eq_multiset_prod, Multiset.toFinset_val, multiset.dedup_eq_self.mpr,
    Multiset.map_id]
  · exact nodup_roots hfx.map
  · intro x
    rfl
  · intro σ
    rw [pb.lift_equiv'_apply_coe, id.def]
#align algebra.norm_eq_prod_embeddings_gen Algebra.norm_eq_prod_embeddings_gen

/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
theorem norm_eq_prod_roots [IsSeparable K L] [FiniteDimensional K L] {x : L}
    (hF : (minpoly K x).Splits (algebraMap K F)) :
    algebraMap K F (norm K x) = ((minpoly K x).map (algebraMap K F)).roots.Prod ^ finrank K⟮⟯ L :=
  by
  rw [norm_eq_norm_adjoin K x, map_pow, IntermediateField.AdjoinSimple.norm_gen_eq_prod_roots _ hF]
#align algebra.norm_eq_prod_roots Algebra.norm_eq_prod_roots

theorem prod_embeddings_eq_finrank_pow [Algebra L F] [IsScalarTower K L F] [IsAlgClosed E]
    [IsSeparable K F] [FiniteDimensional K F] (pb : PowerBasis K L) :
    (∏ σ : F →ₐ[K] E, σ (algebraMap L F pb.gen)) =
      ((@Finset.univ pb.AlgHom.Fintype).Prod fun σ : L →ₐ[K] E => σ pb.gen) ^ finrank L F :=
  by
  haveI : FiniteDimensional L F := FiniteDimensional.right K L F
  haveI : IsSeparable L F := isSeparable_tower_top_of_isSeparable K L F
  letI : Fintype (L →ₐ[K] E) := PowerBasis.AlgHom.fintype pb
  letI : ∀ f : L →ₐ[K] E, Fintype (@AlgHom L F E _ _ _ _ f.to_ring_hom.to_algebra) := _
  rw [Fintype.prod_equiv algHomEquivSigma (fun σ : F →ₐ[K] E => _) fun σ => σ.1 pb.gen, ←
    Finset.univ_sigma_univ, Finset.prod_sigma, ← Finset.prod_pow]
  refine' Finset.prod_congr rfl fun σ _ => _
  · letI : Algebra L E := σ.to_ring_hom.to_algebra
    simp only [Finset.prod_const, Finset.card_univ]
    congr
    rw [AlgHom.card L F E]
  · intro σ
    simp only [algHomEquivSigma, Equiv.coe_fn_mk, AlgHom.restrictDomain, AlgHom.comp_apply,
      IsScalarTower.coe_to_alg_hom']
#align algebra.prod_embeddings_eq_finrank_pow Algebra.prod_embeddings_eq_finrank_pow

variable (K)

/- ./././Mathport/Syntax/Translate/Expr.lean:192:11: unsupported (impossible) -/
/-- For `L/K` a finite separable extension of fields and `E` an algebraically closed extension
of `K`, the norm (down to `K`) of an element `x` of `L` is equal to the product of the images
of `x` over all the `K`-embeddings `σ`  of `L` into `E`. -/
theorem norm_eq_prod_embeddings [FiniteDimensional K L] [IsSeparable K L] [IsAlgClosed E] {x : L} :
    algebraMap K E (norm K x) = ∏ σ : L →ₐ[K] E, σ x :=
  by
  have hx := IsSeparable.isIntegral K x
  rw [norm_eq_norm_adjoin K x, RingHom.map_pow, ← adjoin.power_basis_gen hx,
    norm_eq_prod_embeddings_gen E (adjoin.power_basis hx) (IsAlgClosed.splits_codomain _)]
  · exact (prod_embeddings_eq_finrank_pow L E (adjoin.power_basis hx)).symm
  · haveI := isSeparable_tower_bot_of_isSeparable K K⟮⟯ L
    exact IsSeparable.separable K _
#align algebra.norm_eq_prod_embeddings Algebra.norm_eq_prod_embeddings

theorem norm_eq_prod_automorphisms [FiniteDimensional K L] [IsGalois K L] (x : L) :
    algebraMap K L (norm K x) = ∏ σ : L ≃ₐ[K] L, σ x :=
  by
  apply NoZeroSMulDivisors.algebraMap_injective L (AlgebraicClosure L)
  rw [map_prod (algebraMap L (AlgebraicClosure L))]
  rw [← Fintype.prod_equiv (Normal.algHomEquivAut K (AlgebraicClosure L) L)]
  · rw [← norm_eq_prod_embeddings]
    simp only [algebra_map_eq_smul_one, smul_one_smul]
  · intro σ
    simp only [Normal.algHomEquivAut, AlgHom.restrictNormal', Equiv.coe_fn_mk,
      AlgEquiv.coe_ofBijective, AlgHom.restrictNormal_commutes, id.map_eq_id, RingHom.id_apply]
#align algebra.norm_eq_prod_automorphisms Algebra.norm_eq_prod_automorphisms

theorem isIntegral_norm [Algebra R L] [Algebra R K] [IsScalarTower R K L] [IsSeparable K L]
    [FiniteDimensional K L] {x : L} (hx : IsIntegral R x) : IsIntegral R (norm K x) :=
  by
  have hx' : IsIntegral K x := isIntegral_of_isScalarTower hx
  rw [← isIntegral_algebraMap_iff (algebraMap K (AlgebraicClosure L)).Injective, norm_eq_prod_roots]
  · refine' (IsIntegral.multiset_prod fun y hy => _).pow _
    rw [mem_roots_map (minpoly.ne_zero hx')] at hy
    use minpoly R x, minpoly.monic hx
    rw [← aeval_def] at hy⊢
    exact minpoly.aeval_of_isScalarTower R x y hy
  · apply IsAlgClosed.splits_codomain
  · infer_instance
#align algebra.is_integral_norm Algebra.isIntegral_norm

end EqProdEmbeddings

end Algebra


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Reid Barton, Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.over
import Mathlib.category_theory.limits.connected
import Mathlib.category_theory.limits.creates
import Mathlib.PostPort

universes u v

namespace Mathlib

/-!
# Connected limits in the over category

Shows that the forgetful functor `over B ⥤ C` creates connected limits, in particular `over B` has
any connected limit which `C` has.
-/

namespace category_theory.over

namespace creates_connected

/--
(Impl) Given a diagram in the over category, produce a natural transformation from the
diagram legs to the specific object.
-/
def nat_trans_in_over {J : Type v} [small_category J] {C : Type u} [category C] {B : C} (F : J ⥤ over B) : F ⋙ forget B ⟶ functor.obj (functor.const J) B :=
  nat_trans.mk fun (j : J) => comma.hom (functor.obj F j)

/--
(Impl) Given a cone in the base category, raise it to a cone in the over category. Note this is
where the connected assumption is used.
-/
@[simp] theorem raise_cone_π_app {J : Type v} [small_category J] {C : Type u} [category C] [is_connected J] {B : C} {F : J ⥤ over B} (c : limits.cone (F ⋙ forget B)) (j : J) : nat_trans.app (limits.cone.π (raise_cone c)) j = hom_mk (nat_trans.app (limits.cone.π c) j) :=
  Eq.refl (nat_trans.app (limits.cone.π (raise_cone c)) j)

theorem raised_cone_lowers_to_original {J : Type v} [small_category J] {C : Type u} [category C] [is_connected J] {B : C} {F : J ⥤ over B} (c : limits.cone (F ⋙ forget B)) (t : limits.is_limit c) : functor.map_cone (forget B) (raise_cone c) = c := sorry

/-- (Impl) Show that the raised cone is a limit. -/
def raised_cone_is_limit {J : Type v} [small_category J] {C : Type u} [category C] [is_connected J] {B : C} {F : J ⥤ over B} {c : limits.cone (F ⋙ forget B)} (t : limits.is_limit c) : limits.is_limit (raise_cone c) :=
  limits.is_limit.mk fun (s : limits.cone F) => hom_mk (limits.is_limit.lift t (functor.map_cone (forget B) s))

end creates_connected

/-- The forgetful functor from the over category creates any connected limit. -/
protected instance forget_creates_connected_limits {J : Type v} [small_category J] {C : Type u} [category C] [is_connected J] {B : C} : creates_limits_of_shape J (forget B) :=
  creates_limits_of_shape.mk
    fun (K : J ⥤ over B) =>
      creates_limit_of_reflects_iso
        fun (c : limits.cone (K ⋙ forget B)) (t : limits.is_limit c) =>
          lifts_to_limit.mk
            (liftable_cone.mk (creates_connected.raise_cone c)
              (eq_to_iso (creates_connected.raised_cone_lowers_to_original c t)))
            (creates_connected.raised_cone_is_limit t)

/-- The over category has any connected limit which the original category has. -/
protected instance has_connected_limits {J : Type v} [small_category J] {C : Type u} [category C] {B : C} [is_connected J] [limits.has_limits_of_shape J C] : limits.has_limits_of_shape J (over B) :=
  limits.has_limits_of_shape.mk fun (F : J ⥤ over B) => has_limit_of_created F (forget B)


import data.finset data.set tactic
import data.setoid.partition
import data.finset
import data.fin

namespace variability

def n := 3

@[simp, reducible]
def Feature := fin n

@[derive decidable_eq]
inductive FeatureExpr
| All  : FeatureExpr
| None : FeatureExpr
| Atom : Feature → FeatureExpr
| Not  : FeatureExpr → FeatureExpr
| And  : FeatureExpr → FeatureExpr → FeatureExpr
| Or   : FeatureExpr → FeatureExpr → FeatureExpr

open FeatureExpr
def featExpr_to_string  : FeatureExpr → string
| All       := "Universe"
| None      := "∅"
| (Atom f)  := to_string f --Feature Feature_has_to_string f
| (Not p)   := "¬" ++ featExpr_to_string p
| (And p q) := (featExpr_to_string p) ++ " ∧ " ++ (featExpr_to_string q)
| (Or p q)  := (featExpr_to_string p) ++ " ∨ " ++ (featExpr_to_string q)

instance: has_to_string FeatureExpr := ⟨featExpr_to_string⟩

instance [has_to_string Feature] : has_repr FeatureExpr := ⟨to_string⟩

--#print has_lift_t
@[reducible, simp] --, derive (has_lift (finset Feature) (set Feature))]
def Config: Type := finset Feature

@[reducible, simp]
def ConfigSpace :Type := finset Config

open FeatureExpr
def semantics: FeatureExpr → ConfigSpace
| (All)         := finset.univ
| (None)        := ∅
| (Atom f)      := finset.filter (λx, f ∈ x) finset.univ
| (Not pc)      := finset.univ \ (semantics pc)
| (And pc₁ pc₂) := (semantics pc₁) ∩ (semantics pc₂)
| (Or  pc₁ pc₂) := (semantics pc₁) ∪ (semantics pc₂)

notation `⦃` p `⦄` := semantics p

instance : has_equiv FeatureExpr :=
⟨λ x y, eq ⦃x⦄ ⦃y⦄⟩

def PC := FeatureExpr

instance: has_to_string PC := ⟨featExpr_to_string⟩

structure SPL :=
(Features: Type)
(Φ: FeatureExpr)

open list

--def PCond := @PC F d ft

-- decision procedure for list disjointness
@[simp]
def checkDisj {β : Type} [decidable_eq β]: list (finset β) → bool
| ([]) := true
| (x :: xs) := band (checkDisj xs) (∀ y ∈ xs, x ∩ y = ∅)

@[simp]
def checkTotal (cs : list ConfigSpace) : bool :=
    foldl (∪) ∅ cs = finset.univ

--def C := @Config F d ft
def l {C : Type} := @lift_t (finset C) (set C)

--def m (p : @PC F d ft) : set (@Config F d ft) := ↑(semantics p)

--@[derive [decidable_eq]]

@[reducible]
lemma hl1: has_lift_t (finset Config) (set Config) := ⟨λ x, ↑x⟩

--⟨λ x, let y := finset.image ((λy, ↑y) : finset Feature → set Feature) x in y⟩ --{z:α | z ∈ y}}⟩
@[reducible]
lemma hl2: has_lift_t (finset (finset Config)) (set (set Config)) :=
    ⟨λ x, (finset.image (λ(y: finset Config), @lift_t (finset Config) (set Config) hl1 y) x)⟩

structure ConfigPartition :=
(pcs : list PC)
(cs  : list (finset Config) := (list.map semantics pcs))
(inv: checkDisj(cs) ∧ checkTotal(cs) . tactic.exact_dec_trivial)
--(inv : ∀ (c: Config), ∃! b ∈ cs, c ∈ b . tactic.exact_dec_trivial)

--lemma partition_inv :
--    ∀ (cs : list (finset Conf)), checkDisj cs ∧ checkTotal cs ↔ ∀ (c: @ConfigSpace F d ft), ∃! b ∈ cs, c ∈ b :=
--begin
--    intros, split, induction cs,
      -- base case
--      intros H c, simp at H, simp,
--end

--(disj: checkDisj (list.map semantics (pcs)) . tactic.exact_dec_trivial) --disjPCs pcs)
--(total : checkTotal pcs . tactic.exact_dec_trivial)

--def partition_to_setoid (cp : ConfigPartition) : setoid Config :=
--setoid.mk_classes (↑cp.cs) (cp.inv)

structure Var {α : Type} :=
(p      : ConfigPartition)
(elems  : list α)
(inv    : elems.length = p.pcs.length . tactic.exact_dec_trivial)

--open list
-- configuration partition: ConfigPartition
--def ConfigPartition := indexed_partition (λ pc, {x | x ∈ ⦃pc⦄})

inductive disjPCs : list PC → Prop
| disjPCs_nil : disjPCs []
| disjPCs_cons {x : PC} {xs : list PC} : disjPCs xs → (∀ y, y ∈ xs → ⦃x⦄ ∩ ⦃y⦄ = ∅) → disjPCs (x :: xs)

instance: has_to_string ConfigPartition :=
    ⟨λ p, list.to_string p.pcs⟩

instance: has_repr ConfigPartition := ⟨to_string⟩

def is_eqv (p : ConfigPartition) (ρ₁ ρ₂ : Config) : bool :=
let idx := λ ρ, list.find (λ x, ρ ∈ ⦃x⦄) p.pcs
in  idx ρ₁ = idx ρ₂

local infix `~` := is_eqv
/-
lemma checkDisj_correct :
    ∀ (pcs : list PC), disjPCs pcs ↔ (checkDisj (map semantics pcs) = tt) :=
begin
    intros, split,
    -- →
    intro h, induction h,
      -- base case
      simp,
      -- induction
      simp, split,
        -- lhs
        apply h_ih,
        -- rhs
        apply h_ᾰ_1,
    -- ←
    intro h, induction pcs,
      -- base case
      apply disjPCs.disjPCs_nil,
      -- induction
      apply disjPCs.disjPCs_cons,
        apply pcs_ih, unfold map at h, simp at h, apply h.left,
        intros y h₂, simp at h, apply h.right, apply h₂
end
-/

section LiftedVals
/-
parameter {α :Type}

-- annotated value
def annVal := (α × PC)

-- lifted value
structure Var :=
    (elems: list annVal)
    (disj: checkDisj (map semantics (map prod.snd elems)) . tactic.exact_dec_trivial)
    (total : checkTotal (map prod.snd elems) . tactic.exact_dec_trivial)

instance {α : Type} [has_to_string (α × PC)]: has_to_string (@Var α) :=
    ⟨λ v, list.to_string v.elems⟩

instance {α : Type} [has_to_string (α × PC)]: has_repr (@Var α) := ⟨to_string⟩

local postfix `↑`:(max+1) := Var
-/
end LiftedVals

def index' {α:Type} (ρ : Config) : list (α × PC) → option α
| [] := none
| (x :: xs) := if ρ ∈ ⦃x.2⦄ then some x.1 else index' xs

def index {α:Type} (ρ : Config) (v : @Var α) : option α :=
    index' ρ (list.zip v.elems v.p.pcs)

infix `|` := index

/-
/-
def cover {Feature : Type} [t : fintype Feature] [d : decidable_eq Feature] {α : Type}
    (v : list (@Var Feature t α)) : finset Config :=
    semantics(foldr Or None (map Var.pc v))

inductive disjointVars {Feature : Type} [t : fintype Feature] [d : decidable_eq Feature] {α : Type} : (list (@Var Feature t α)) → Prop
| nil_disjoint : disjointVars []
| cons_disjoint : ∀ (x : @Var Feature t α) (xs: list (@Var Feature t α)), ⟦x.pc⟧ ∩ cover xs = ∅ → disjointVars (x :: xs)
-/

def disjointPCs {α : Type} (s: list (@AVal α)) : Prop :=
    ∀ (c : Config) (pc₁ pc₂ : PC), pc₁ ∈ map (AVal.pc) s → pc₂ ∈ map (AVal.pc) s →
                                   c ∈ semantics pc₁ → c ∈ semantics pc₂ → pc₁ = pc₂

def completePCs {α : Type} (s: list (@AVal α)) : Prop :=
    ∀ (c : Config), ∃ (v : AVal), v ∈ s ∧ c ∈ semantics v.pc

structure Var {α : Type} :=
    (s    : list (@AVal α))
    (disj : disjointPCs s)
    (comp : completePCs s)

section

open classical

lemma list_find {α : Type} (q : α → Prop) [decidable_pred q] : ∀ (l : list α),
                            (∃ y, y ∈ l ∧ q y) → (list.find q l).is_some :=
begin
    intros l h₁, --apply exists.elim h₁, intros a h₂,
    induction l,
    -- base case
    apply exists.elim h₁, intros a h₂, apply and.elim h₂, intros h₃ h₄, simp,
    apply not_mem_nil a h₃,
    -- induction
    unfold find, cases decidable.em (q l_hd) with hpos hneg,
    { rw if_pos hpos, simp },
    { rw if_neg hneg, apply exists.elim h₁, intros a h₂, apply l_ih, apply exists.intro a,
      simp at h₂, rw and.comm at h₂, rw and_or_distrib_left at h₂, apply or.elim h₂,
      {intro h₃, apply and.elim h₃, intros h₄ h₅, rw← h₅ at hneg, contradiction},
      {intro h₃, rw and.comm, exact h₃}
    }
end

end --section

lemma index_unique {α : Type} (l1 : @Var α) (x : @AVal α) (ρ : Config)
    : x ∈ l.s → ρ ∈ semantics x.pc → (index l ρ) = x.v :=
begin
    intros h1 h2,
    unfold index, simp, split_ifs,
    {rw option.get_of_mem, simp,  }
end
#print list.find_mem

/-
def PCPartition {Feature: Type} [t : fintype Feature] [d: decidable_eq Feature] {α : Type}
    (v : @Lifted Feature t d α) : list (@PC Feature) :=
    map (Var.pc) v.s
-/

def apply' {Feature : Type} [t: fintype Feature] [d: decidable_eq Feature] {α β : Type}
    (f : @Lifted Feature t d (α → β)) (v : @Lifted Feature t d α) : list (@Var Feature t β) :=
    let pairs := f.s.product v.s,
        satPairs := filter (λ (p : ((@Var Feature t (α → β)) × Var α)) , ⟦p.1.pc⟧ ∩ ⟦p.2.pc⟧ ≠ ∅) pairs
    in  map (λ (p : (Var (α → β) × Var α)), Var.mk (p.1.v p.2.v) (And p.1.pc p.2.pc)) satPairs

lemma apply'_disjoint {Feature : Type} [t: fintype Feature] [d: decidable_eq Feature] {α β : Type} :
    ∀ (f : @Lifted Feature t d (α → β)) (v : @Lifted Feature t d α), disjointPCs (apply' f v) :=
begin
    intros f v, unfold apply', simp, unfold disjointPCs, intros c pc₁ pc₂ h1 h2 h3 h4,
    simp at h1, simp at h2, apply exists.elim h1, intros, apply exists.elim ᾰ, intros,
    apply and.elim ᾰ_1, intros, apply and.elim ᾰ_2, intros, apply and.elim ᾰ_4, intros,
    apply exists.elim h2, intros, apply exists.elim ᾰ_8, intros,
    apply and.elim ᾰ_9, intros, apply and.elim ᾰ_10, intros, apply and.elim ᾰ_12, intros,
    rw← ᾰ_3 at h3, unfold semantics at h3, simp at h3,
    rw← ᾰ_11 at h4, unfold semantics at h4, simp at h4,
    rw← ᾰ_3, rw← ᾰ_11, have : a.pc = a_2.pc,
    apply f.disj, simp, existsi a, split, apply ᾰ_6, refl,
    simp, existsi a_2, split, apply ᾰ_14, refl,
    apply h3.left, apply h4.left,
    rw this, have : a_1.pc = a_3.pc,
    apply v.disj, simp, existsi a_1, split, apply ᾰ_7, refl,
    simp, existsi a_3, split, apply ᾰ_15, refl,
    apply exists.elim h1, intros a h5, apply exists.elim h5,
    intros a_1 h6, apply and.elim h6, intros h7 h8, apply h3.right, apply h4.right,
    rw this
end

lemma apply'_complete {Feature : Type} [t: fintype Feature] [d: decidable_eq Feature] {α β : Type} :
    ∀ (f : @Lifted Feature t d (α → β)) (v : @Lifted Feature t d α), completePCs (apply' f v) :=
begin
    intros f v, unfold apply', simp, unfold completePCs, intros, simp,
    have : ∃ (v₁ : Var (α → β)), v₁ ∈ f.s ∧ c ∈ ⟦v₁.pc⟧, apply f.comp, apply exists.elim this, intros,
    have : ∃ (v₂ : Var α), v₂ ∈ v.s ∧ c ∈ ⟦v₂.pc⟧, apply v.comp, apply exists.elim this, intros,
    existsi (Var.mk (a.v a_1.v) (a.pc.And a_1.pc)), simp, split,
    existsi a, existsi a_1, split, split, split, exact ᾰ.left, exact ᾰ_1.left,
    apply finset.ne_empty_of_mem, simp, split, apply ᾰ.right, apply ᾰ_1.right,
    simp, unfold semantics, simp, split, apply ᾰ.right, apply ᾰ_1.right
end

def apply {Feature : Type} [t: fintype Feature] [d: decidable_eq Feature] {α β : Type}
    (f : @Lifted Feature t d (α → β)) (v : @Lifted Feature t d α) : @Lifted Feature t d β :=
⟨apply' f v, apply'_disjoint f v, apply'_complete f v⟩

theorem apply_correct {Feature : Type} [t: fintype Feature] [d: decidable_eq Feature] (α β : Type) :
    ∀ (f : @Lifted Feature t d (α → β)) (v : @Lifted Feature t d α) (ρ : @Config Feature t),
    index (apply f v) ρ = (index f ρ) (index v ρ) :=
begin
    intros f v ρ,
end
-/
end variability


import order.lattice data.nat.gcd tactic tactic.explode

class lattice' (α : Type*) extends lattice.has_inf α, lattice.has_sup α :=
(inf_comm : ∀ x y : α, x ⊓ y = y ⊓ x)
(inf_assoc : ∀ x y z : α, x ⊓ y ⊓ z = x ⊓ (y ⊓ z))
(sup_comm : ∀ x y : α, x ⊔ y = y ⊔ x)
(sup_assoc : ∀ x y z : α, x ⊔ y ⊔ z = x ⊔ (y ⊔ z))
(inf_absorb : ∀ x y : α, x ⊓ (x ⊔ y) = x)
(sup_absorb : ∀ x y : α, x ⊔ (x ⊓ y) = x)

namespace lattice'

attribute [ematch] inf_comm inf_assoc sup_comm sup_assoc inf_absorb sup_absorb

-- inside the namespace, you can refer to the axioms without a prefix

variables {α : Type*} [lattice' α]

lemma try_me (x : α) : x ⊔ x = x :=
begin
  conv {to_lhs, congr, skip, rw[<-inf_absorb x x]},
  rw[sup_absorb x _]
end

@[ematch]theorem sup_idem (x : α) : x ⊔ x = x := by finish

@[ematch]theorem inf_idem (x : α) : x ⊓ x = x := by finish

protected def le (x y : α) := x = x ⊓ y

instance : has_le α := ⟨lattice'.le⟩

@[ematch]lemma le_unfold {x y : α} : x ≤ y ↔ x = x ⊓ y := by refl

@[ematch]theorem le_def (x y : α) : (x ≤ y) = (x = x ⊓ y) := rfl

@[ematch]theorem le_refl (x : α) : x ≤ x := by rw [le_def, inf_idem]

-- you can use `rw le_def at *` to unfold the definition everywhere

-- Wikipedia also tells you how to prove this one:

@[ematch]theorem le_def' (x y : α) : (x ≤ y) ↔ (y = x ⊔ y) :=
by split; intros; finish

@[ematch]theorem le_trans {x y z : α} (h : x ≤ y) (h' : y ≤ z) : x ≤ z := by finish

@[ematch]theorem le_antisymm {x y : α} (h : x ≤ y) (h' : y ≤ x) : x = y := by finish

@[ematch]theorem le_sup_left (x y : α) : x ≤ x ⊔ y := by finish

@[ematch]theorem le_sup_right (x y : α) : y ≤ x ⊔ y := by finish

@[ematch]theorem sup_le {x y z : α} (h₁ : x ≤ z) (h₂ : y ≤ z) : x ⊔ y ≤ z := by finish

@[ematch]theorem inf_le_left (x y : α) : x ⊓ y ≤ x := by finish

@[ematch]theorem inf_le_right (x y : α) : x ⊓ y ≤ y := by finish

@[ematch]theorem le_inf {x y z : α} (h₁ : x ≤ y) (h₂ : x ≤ z) : x ≤ y ⊓ z := by finish

end lattice'

-- instance to_lattice : lattice.lattice α :=
-- { sup           := λ x y, x ⊔ y,
--   inf           := λ x y, x ⊓ y,
--    le           := lattice'.le,
--    le_refl      := le_refl,
--    le_trans     := @le_trans _ _,
--    le_antisymm  := @le_antisymm _ _,
--    le_sup_left  := le_sup_left,
--    le_sup_right := le_sup_right,
--    sup_le       := @sup_le _ _,
--    inf_le_left  := inf_le_left,
--    inf_le_right := inf_le_right,
--    le_inf       := @le_inf _ _ }

-- end lattice'

/- This part is required of graduate students only. It shows that, conversely,
   a lattice is a lattice'.

   To do this, you will want to take a look at the theorems in order.lattice.
-/

namespace lattice

variables {α : Type*} [lattice α]

theorem inf_absorb (x y : α) : x ⊓ (x ⊔ y) = x :=
by {apply le_antisymm; finish}

theorem sup_absorb (x y : α) : x ⊔ (x ⊓ y) = x :=
by {apply le_antisymm; finish}

-- For this, you will want to you use the badly named theorem, `inf_of_le_left`.
-- (It should be named `inf_eq_of_le_left`.)

theorem le_iff (x y : α) : x ≤ y ↔ x = x ⊓ y :=
by {split; intros, {apply le_antisymm; finish}, finish}

end lattice

/-
Optional: show that the natural numbers form a lattice where the ordering
is the divsibility relation, sup is the least common multiple, and inf is
the greatest common divisor.

To do this, look at the theorems in data.nat.gcd.

Remember, to type `x ∣ y`, use `\|`.
-/

section
open lattice nat

instance nat_div_lattice : lattice nat :=
{ le           := λ x y, x ∣ y,
  sup          := lcm,
  inf          := gcd,
  le_refl      := sorry,
  le_trans     := sorry,
  le_antisymm  := sorry,
  le_sup_left  := sorry,
  le_sup_right := sorry,
  sup_le       := sorry,
  inf_le_left  := sorry,
  inf_le_right := sorry,
  le_inf       := sorry }

end


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

! This file was ported from Lean 3 source module topology.algebra.order.t5
! leanprover-community/mathlib commit 50832daea47b195a48b5b33b1c8b2162c48c3afc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Order.Basic
import Mathbin.Data.Set.Intervals.OrdConnectedComponent

/-!
# Linear order is a completely normal Hausdorff topological space

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

In this file we prove that a linear order with order topology is a completely normal Hausdorff
topological space.
-/

open Filter Set Function OrderDual

open Topology Filter Interval

variable {X : Type _} [LinearOrder X] [TopologicalSpace X] [OrderTopology X] {a b c : X}
  {s t : Set X}

namespace Set

#print Set.ordConnectedComponent_mem_nhds /-
@[simp]
theorem ordConnectedComponent_mem_nhds : ordConnectedComponent s a ∈ 𝓝 a ↔ s ∈ 𝓝 a :=
  by
  refine' ⟨fun h => mem_of_superset h ord_connected_component_subset, fun h => _⟩
  rcases exists_Icc_mem_subset_of_mem_nhds h with ⟨b, c, ha, ha', hs⟩
  exact mem_of_superset ha' (subset_ord_connected_component ha hs)
#align set.ord_connected_component_mem_nhds Set.ordConnectedComponent_mem_nhds
-/

/- warning: set.compl_section_ord_separating_set_mem_nhds_within_Ici -> Set.compl_section_ordSeparatingSet_mem_nhdsWithin_Ici is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (LinearOrder.toLattice.{u1} X _inst_1))))] {a : X} {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.completeBooleanAlgebra.{u1} X)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} X) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X))) s (closure.{u1} X _inst_2 t)) -> (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) -> (Membership.Mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (Filter.hasMem.{u1} X) (HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X)) (Set.ordConnectedSection.{u1} X _inst_1 (Set.ordSeparatingSet.{u1} X _inst_1 s t))) (nhdsWithin.{u1} X _inst_2 a (Set.Ici.{u1} X (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (LinearOrder.toLattice.{u1} X _inst_1)))) a)))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (DistribLattice.toLattice.{u1} X (instDistribLattice.{u1} X _inst_1)))))] {a : X} {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} X) (Preorder.toLE.{u1} (Set.{u1} X) (PartialOrder.toPreorder.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) s (closure.{u1} X _inst_2 t)) -> (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) -> (Membership.mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (instMembershipSetFilter.{u1} X) (HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.instBooleanAlgebraSet.{u1} X)) (Set.ordConnectedSection.{u1} X _inst_1 (Set.ordSeparatingSet.{u1} X _inst_1 s t))) (nhdsWithin.{u1} X _inst_2 a (Set.Ici.{u1} X (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (DistribLattice.toLattice.{u1} X (instDistribLattice.{u1} X _inst_1))))) a)))
Case conversion may be inaccurate. Consider using '#align set.compl_section_ord_separating_set_mem_nhds_within_Ici Set.compl_section_ordSeparatingSet_mem_nhdsWithin_Iciₓ'. -/
theorem compl_section_ordSeparatingSet_mem_nhdsWithin_Ici (hd : Disjoint s (closure t))
    (ha : a ∈ s) : (ordConnectedSection <| ordSeparatingSet s t)ᶜ ∈ 𝓝[≥] a :=
  by
  have hmem : tᶜ ∈ 𝓝[≥] a := by
    refine' mem_nhdsWithin_of_mem_nhds _
    rw [← mem_interior_iff_mem_nhds, interior_compl]
    exact disjoint_left.1 hd ha
  rcases exists_Icc_mem_subset_of_mem_nhdsWithin_Ici hmem with ⟨b, hab, hmem', hsub⟩
  by_cases H : Disjoint (Icc a b) (ord_connected_section <| ord_separating_set s t)
  · exact mem_of_superset hmem' (disjoint_left.1 H)
  · simp only [Set.disjoint_left, not_forall, Classical.not_not] at H
    rcases H with ⟨c, ⟨hac, hcb⟩, hc⟩
    have hsub' : Icc a b ⊆ ord_connected_component (tᶜ) a :=
      subset_ord_connected_component (left_mem_Icc.2 hab) hsub
    replace hac : a < c :=
      hac.lt_of_ne
        (Ne.symm <|
          ne_of_mem_of_not_mem hc <|
            disjoint_left.1
              (disjoint_left_ord_separating_set.mono_right ord_connected_section_subset) ha)
    refine' mem_of_superset (Ico_mem_nhdsWithin_Ici (left_mem_Ico.2 hac)) fun x hx hx' => _
    refine' hx.2.Ne (eq_of_mem_ord_connected_section_of_uIcc_subset hx' hc _)
    refine' subset_inter (subset_Union₂_of_subset a ha _) _
    ·
      exact
        ord_connected.uIcc_subset inferInstance (hsub' ⟨hx.1, hx.2.le.trans hcb⟩)
          (hsub' ⟨hac.le, hcb⟩)
    · rcases mem_Union₂.1 (ord_connected_section_subset hx').2 with ⟨y, hyt, hxy⟩
      refine' subset_Union₂_of_subset y hyt (ord_connected.uIcc_subset inferInstance hxy _)
      refine' subset_ord_connected_component left_mem_uIcc hxy _
      suffices c < y by
        rw [uIcc_of_ge (hx.2.trans this).le]
        exact ⟨hx.2.le, this.le⟩
      refine' lt_of_not_le fun hyc => _
      have hya : y < a := not_le.1 fun hay => hsub ⟨hay, hyc.trans hcb⟩ hyt
      exact hxy (Icc_subset_uIcc ⟨hya.le, hx.1⟩) ha
#align set.compl_section_ord_separating_set_mem_nhds_within_Ici Set.compl_section_ordSeparatingSet_mem_nhdsWithin_Ici

/- warning: set.compl_section_ord_separating_set_mem_nhds_within_Iic -> Set.compl_section_ordSeparatingSet_mem_nhdsWithin_Iic is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (LinearOrder.toLattice.{u1} X _inst_1))))] {a : X} {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.completeBooleanAlgebra.{u1} X)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} X) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X))) s (closure.{u1} X _inst_2 t)) -> (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) -> (Membership.Mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (Filter.hasMem.{u1} X) (HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X)) (Set.ordConnectedSection.{u1} X _inst_1 (Set.ordSeparatingSet.{u1} X _inst_1 s t))) (nhdsWithin.{u1} X _inst_2 a (Set.Iic.{u1} X (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (LinearOrder.toLattice.{u1} X _inst_1)))) a)))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (DistribLattice.toLattice.{u1} X (instDistribLattice.{u1} X _inst_1)))))] {a : X} {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} X) (Preorder.toLE.{u1} (Set.{u1} X) (PartialOrder.toPreorder.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) s (closure.{u1} X _inst_2 t)) -> (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) -> (Membership.mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (instMembershipSetFilter.{u1} X) (HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.instBooleanAlgebraSet.{u1} X)) (Set.ordConnectedSection.{u1} X _inst_1 (Set.ordSeparatingSet.{u1} X _inst_1 s t))) (nhdsWithin.{u1} X _inst_2 a (Set.Iic.{u1} X (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (DistribLattice.toLattice.{u1} X (instDistribLattice.{u1} X _inst_1))))) a)))
Case conversion may be inaccurate. Consider using '#align set.compl_section_ord_separating_set_mem_nhds_within_Iic Set.compl_section_ordSeparatingSet_mem_nhdsWithin_Iicₓ'. -/
theorem compl_section_ordSeparatingSet_mem_nhdsWithin_Iic (hd : Disjoint s (closure t))
    (ha : a ∈ s) : (ordConnectedSection <| ordSeparatingSet s t)ᶜ ∈ 𝓝[≤] a :=
  by
  have hd' : Disjoint (ofDual ⁻¹' s) (closure <| ofDual ⁻¹' t) := hd
  have ha' : toDual a ∈ ofDual ⁻¹' s := ha
  simpa only [dual_ord_separating_set, dual_ord_connected_section] using
    compl_section_ord_separating_set_mem_nhds_within_Ici hd' ha'
#align set.compl_section_ord_separating_set_mem_nhds_within_Iic Set.compl_section_ordSeparatingSet_mem_nhdsWithin_Iic

/- warning: set.compl_section_ord_separating_set_mem_nhds -> Set.compl_section_ordSeparatingSet_mem_nhds is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (LinearOrder.toLattice.{u1} X _inst_1))))] {a : X} {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.completeBooleanAlgebra.{u1} X)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} X) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X))) s (closure.{u1} X _inst_2 t)) -> (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) a s) -> (Membership.Mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (Filter.hasMem.{u1} X) (HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X)) (Set.ordConnectedSection.{u1} X _inst_1 (Set.ordSeparatingSet.{u1} X _inst_1 s t))) (nhds.{u1} X _inst_2 a))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (DistribLattice.toLattice.{u1} X (instDistribLattice.{u1} X _inst_1)))))] {a : X} {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} X) (Preorder.toLE.{u1} (Set.{u1} X) (PartialOrder.toPreorder.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) s (closure.{u1} X _inst_2 t)) -> (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) a s) -> (Membership.mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (instMembershipSetFilter.{u1} X) (HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.instBooleanAlgebraSet.{u1} X)) (Set.ordConnectedSection.{u1} X _inst_1 (Set.ordSeparatingSet.{u1} X _inst_1 s t))) (nhds.{u1} X _inst_2 a))
Case conversion may be inaccurate. Consider using '#align set.compl_section_ord_separating_set_mem_nhds Set.compl_section_ordSeparatingSet_mem_nhdsₓ'. -/
theorem compl_section_ordSeparatingSet_mem_nhds (hd : Disjoint s (closure t)) (ha : a ∈ s) :
    (ordConnectedSection <| ordSeparatingSet s t)ᶜ ∈ 𝓝 a :=
  by
  rw [← nhds_left_sup_nhds_right, mem_sup]
  exact
    ⟨compl_section_ord_separating_set_mem_nhds_within_Iic hd ha,
      compl_section_ord_separating_set_mem_nhds_within_Ici hd ha⟩
#align set.compl_section_ord_separating_set_mem_nhds Set.compl_section_ordSeparatingSet_mem_nhds

/- warning: set.ord_t5_nhd_mem_nhds_set -> Set.ordT5Nhd_mem_nhdsSet is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (LinearOrder.toLattice.{u1} X _inst_1))))] {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.completeBooleanAlgebra.{u1} X)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} X) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} X) (Set.booleanAlgebra.{u1} X))) s (closure.{u1} X _inst_2 t)) -> (Membership.Mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (Filter.hasMem.{u1} X) (Set.ordT5Nhd.{u1} X _inst_1 s t) (nhdsSet.{u1} X _inst_2 s))
but is expected to have type
  forall {X : Type.{u1}} [_inst_1 : LinearOrder.{u1} X] [_inst_2 : TopologicalSpace.{u1} X] [_inst_3 : OrderTopology.{u1} X _inst_2 (PartialOrder.toPreorder.{u1} X (SemilatticeInf.toPartialOrder.{u1} X (Lattice.toSemilatticeInf.{u1} X (DistribLattice.toLattice.{u1} X (instDistribLattice.{u1} X _inst_1)))))] {s : Set.{u1} X} {t : Set.{u1} X}, (Disjoint.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} X) (Preorder.toLE.{u1} (Set.{u1} X) (PartialOrder.toPreorder.{u1} (Set.{u1} X) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} X) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} X) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} X) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} X) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} X) (Set.instCompleteBooleanAlgebraSet.{u1} X)))))) s (closure.{u1} X _inst_2 t)) -> (Membership.mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (instMembershipSetFilter.{u1} X) (Set.ordT5Nhd.{u1} X _inst_1 s t) (nhdsSet.{u1} X _inst_2 s))
Case conversion may be inaccurate. Consider using '#align set.ord_t5_nhd_mem_nhds_set Set.ordT5Nhd_mem_nhdsSetₓ'. -/
theorem ordT5Nhd_mem_nhdsSet (hd : Disjoint s (closure t)) : ordT5Nhd s t ∈ 𝓝ˢ s :=
  bUnion_mem_nhdsSet fun x hx =>
    ordConnectedComponent_mem_nhds.2 <|
      inter_mem
        (by
          rw [← mem_interior_iff_mem_nhds, interior_compl]
          exact disjoint_left.1 hd hx)
        (compl_section_ordSeparatingSet_mem_nhds hd hx)
#align set.ord_t5_nhd_mem_nhds_set Set.ordT5Nhd_mem_nhdsSet

end Set

open Set

#print OrderTopology.t5Space /-
/-- A linear order with order topology is a completely normal Hausdorff topological space. -/
instance (priority := 100) OrderTopology.t5Space : T5Space X :=
  ⟨fun s t h₁ h₂ =>
    Filter.disjoint_iff.2
      ⟨ordT5Nhd s t, ordT5Nhd_mem_nhdsSet h₂, ordT5Nhd t s, ordT5Nhd_mem_nhdsSet h₁.symm,
        disjoint_ordT5Nhd⟩⟩
#align order_topology.t5_space OrderTopology.t5Space
-/


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

! This file was ported from Lean 3 source module analysis.locally_convex.strong_topology
! leanprover-community/mathlib commit b8627dbac120a9ad6267a75575ae1e070d5bff5b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Algebra.Module.StrongTopology
import Mathbin.Topology.Algebra.Module.LocallyConvex

/-!
# Local convexity of the strong topology

In this file we prove that the strong topology on `E →L[ℝ] F` is locally convex provided that `F` is
locally convex.

## References

* [N. Bourbaki, *Topological Vector Spaces*][bourbaki1987]

## Todo

* Characterization in terms of seminorms

## Tags

locally convex, bounded convergence
-/

open Topology UniformConvergence

variable {E F : Type _}

namespace ContinuousLinearMap

section General

variable [AddCommGroup E] [Module ℝ E] [TopologicalSpace E] [AddCommGroup F] [Module ℝ F]
  [TopologicalSpace F] [TopologicalAddGroup F] [ContinuousConstSMul ℝ F] [LocallyConvexSpace ℝ F]

theorem strongTopology.locallyConvexSpace (𝔖 : Set (Set E)) (h𝔖₁ : 𝔖.Nonempty)
    (h𝔖₂ : DirectedOn (· ⊆ ·) 𝔖) :
    @LocallyConvexSpace ℝ (E →L[ℝ] F) _ _ _ (strongTopology (RingHom.id ℝ) F 𝔖) :=
  by
  letI : TopologicalSpace (E →L[ℝ] F) := strong_topology (RingHom.id ℝ) F 𝔖
  haveI : TopologicalAddGroup (E →L[ℝ] F) := strong_topology.topological_add_group _ _ _
  refine'
    LocallyConvexSpace.ofBasisZero _ _ _ _
      (strong_topology.has_basis_nhds_zero_of_basis _ _ _ h𝔖₁ h𝔖₂
        (LocallyConvexSpace.convex_basis_zero ℝ F))
      _
  rintro ⟨S, V⟩ ⟨hS, hVmem, hVconvex⟩ f hf g hg a b ha hb hab x hx
  exact hVconvex (hf x hx) (hg x hx) ha hb hab
#align continuous_linear_map.strong_topology.locally_convex_space ContinuousLinearMap.strongTopology.locallyConvexSpace

end General

section BoundedSets

variable [AddCommGroup E] [Module ℝ E] [TopologicalSpace E] [AddCommGroup F] [Module ℝ F]
  [TopologicalSpace F] [TopologicalAddGroup F] [ContinuousConstSMul ℝ F] [LocallyConvexSpace ℝ F]

instance : LocallyConvexSpace ℝ (E →L[ℝ] F) :=
  strongTopology.locallyConvexSpace _ ⟨∅, Bornology.isVonNBounded_empty ℝ E⟩
    (directedOn_of_sup_mem fun _ _ => Bornology.IsVonNBounded.union)

end BoundedSets

end ContinuousLinearMap


/-
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, Johan Commelin, Mario Carneiro
-/

import data.mv_polynomial.variables

/-!
# Multivariate polynomials over a ring

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

Many results about polynomials hold when the coefficient ring is a commutative semiring.
Some stronger results can be derived when we assume this semiring is a ring.

This file does not define any new operations, but proves some of these stronger results.

## Notation

As in other polynomial files, we typically use the notation:

+ `σ : Type*` (indexing the variables)

+ `R : Type*` `[comm_ring R]` (the coefficients)

+ `s : σ →₀ ℕ`, a function from `σ` to `ℕ` which is zero away from a finite set.
This will give rise to a monomial in `mv_polynomial σ R` which mathematicians might call `X^s`

+ `a : R`

+ `i : σ`, with corresponding monomial `X i`, often denoted `X_i` by mathematicians

+ `p : mv_polynomial σ R`

-/

noncomputable theory

open_locale classical big_operators

open set function finsupp add_monoid_algebra
open_locale big_operators

universes u v
variables {R : Type u} {S : Type v}

namespace mv_polynomial
variables {σ : Type*} {a a' a₁ a₂ : R} {e : ℕ} {n m : σ} {s : σ →₀ ℕ}

section comm_ring
variable [comm_ring R]
variables {p q : mv_polynomial σ R}

instance : comm_ring (mv_polynomial σ R) := add_monoid_algebra.comm_ring

variables (σ a a')

@[simp] lemma C_sub : (C (a - a') : mv_polynomial σ R) = C a - C a' := ring_hom.map_sub _ _ _

@[simp] lemma C_neg : (C (-a) : mv_polynomial σ R) = -C a := ring_hom.map_neg _ _

@[simp] lemma coeff_neg (m : σ →₀ ℕ) (p : mv_polynomial σ R) :
  coeff m (-p) = -coeff m p := finsupp.neg_apply _ _

@[simp] lemma coeff_sub (m : σ →₀ ℕ) (p q : mv_polynomial σ R) :
  coeff m (p - q) = coeff m p - coeff m q := finsupp.sub_apply _ _ _

@[simp] lemma support_neg : (- p).support = p.support :=
finsupp.support_neg p

lemma support_sub (p q : mv_polynomial σ R) : (p - q).support ⊆ p.support ∪ q.support :=
finsupp.support_sub

variables {σ} (p)

section degrees

lemma degrees_neg (p : mv_polynomial σ R) : (- p).degrees = p.degrees :=
by rw [degrees, support_neg]; refl

lemma degrees_sub (p q : mv_polynomial σ R) :
  (p - q).degrees ≤ p.degrees ⊔ q.degrees :=
by simpa only [sub_eq_add_neg] using le_trans (degrees_add p (-q)) (by rw degrees_neg)

end degrees

section vars

variables (p q)

@[simp] lemma vars_neg : (-p).vars = p.vars :=
by simp [vars, degrees_neg]

lemma vars_sub_subset : (p - q).vars ⊆ p.vars ∪ q.vars :=
by convert vars_add_subset p (-q) using 2; simp [sub_eq_add_neg]

variables {p q}

@[simp]
lemma vars_sub_of_disjoint (hpq : disjoint p.vars q.vars) : (p - q).vars = p.vars ∪ q.vars :=
begin
  rw ←vars_neg q at hpq,
  convert vars_add_of_disjoint hpq using 2;
    simp [sub_eq_add_neg]
end

end vars

section eval₂

variables [comm_ring S]
variables (f : R →+* S) (g : σ → S)

@[simp]

@[simp] lemma eval₂_neg : (-p).eval₂ f g = -(p.eval₂ f g) := (eval₂_hom f g).map_neg _

lemma hom_C (f : mv_polynomial σ ℤ →+* S) (n : ℤ) : f (C n) = (n : S) :=
eq_int_cast (f.comp C) n

/-- A ring homomorphism f : Z[X_1, X_2, ...] → R
is determined by the evaluations f(X_1), f(X_2), ... -/
@[simp] lemma eval₂_hom_X {R : Type u} (c : ℤ →+* S)
  (f : mv_polynomial R ℤ →+* S) (x : mv_polynomial R ℤ) :
  eval₂ c (f ∘ X) x = f x :=
mv_polynomial.induction_on x
(λ n, by { rw [hom_C f, eval₂_C], exact eq_int_cast c n })
(λ p q hp hq, by { rw [eval₂_add, hp, hq], exact (f.map_add _ _).symm })
(λ p n hp, by { rw [eval₂_mul, eval₂_X, hp], exact (f.map_mul _ _).symm })

/-- Ring homomorphisms out of integer polynomials on a type `σ` are the same as
functions out of the type `σ`, -/
def hom_equiv : (mv_polynomial σ ℤ →+* S) ≃ (σ → S) :=
{ to_fun := λ f, ⇑f ∘ X,
  inv_fun := λ f, eval₂_hom (int.cast_ring_hom S) f,
  left_inv := λ f, ring_hom.ext  $ eval₂_hom_X _ _,
  right_inv := λ f, funext $ λ x, by simp only [coe_eval₂_hom, function.comp_app, eval₂_X] }

end eval₂

section degree_of

lemma degree_of_sub_lt {x : σ} {f g : mv_polynomial σ R} {k : ℕ} (h : 0 < k)
  (hf : ∀ (m : σ →₀ ℕ), m ∈ f.support → (k ≤ m x) → coeff m f = coeff m g)
  (hg : ∀ (m : σ →₀ ℕ), m ∈ g.support → (k ≤ m x) → coeff m f = coeff m g) :
  degree_of x (f - g) < k :=
begin
  rw degree_of_lt_iff h,
  intros m hm,
  by_contra hc,
  simp only [not_lt] at hc,
  have h := support_sub σ f g hm,
  simp only [mem_support_iff, ne.def, coeff_sub, sub_eq_zero] at hm,
  cases (finset.mem_union).1 h with cf cg,
  { exact hm (hf m cf hc), },
  { exact hm (hg m cg hc), },
end

end degree_of

section total_degree

@[simp] lemma total_degree_neg (a : mv_polynomial σ R) :
  (-a).total_degree = a.total_degree :=
by simp only [total_degree, support_neg]

lemma total_degree_sub (a b : mv_polynomial σ R) :
  (a - b).total_degree ≤ max a.total_degree b.total_degree :=
calc (a - b).total_degree = (a + -b).total_degree                : by rw sub_eq_add_neg
                      ... ≤ max a.total_degree (-b).total_degree : total_degree_add a (-b)
                      ... = max a.total_degree b.total_degree    : by rw total_degree_neg

end total_degree

end comm_ring

end mv_polynomial


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import analysis.special_functions.integrals
import analysis.calculus.fderiv_measurable

/-!
# Non integrable functions

In this file we prove that the derivative of a function that tends to infinity is not interval
integrable, see `interval_integral.not_integrable_has_deriv_at_of_tendsto_norm_at_top_filter` and
`interval_integral.not_integrable_has_deriv_at_of_tendsto_norm_at_top_punctured`.  Then we apply the
latter lemma to prove that the function `λ x, x⁻¹` is integrable on `a..b` if and only if `a = b` or
`0 ∉ [a, b]`.

## Main results

* `not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_punctured`: if `f` tends to infinity
  along `𝓝[≠] c` and `f' = O(g)` along the same filter, then `g` is not interval integrable on any
  nontrivial integral `a..b`, `c ∈ [a, b]`.

* `not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_filter`: a version of
  `not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_punctured` that works for one-sided
  neighborhoods;

* `not_interval_integrable_of_sub_inv_is_O_punctured`: if `1 / (x - c) = O(f)` as `x → c`, `x ≠ c`,
  then `f` is not interval integrable on any nontrivial interval `a..b`, `c ∈ [a, b]`;

* `interval_integrable_sub_inv_iff`, `interval_integrable_inv_iff`: integrability conditions for
  `(x - c)⁻¹` and `x⁻¹`.

## Tags

integrable function
-/

open_locale measure_theory topological_space interval nnreal ennreal
open measure_theory topological_space set filter asymptotics interval_integral

variables {E F : Type*} [normed_group E] [normed_space ℝ E] [measurable_space E] [borel_space E]
  [second_countable_topology E] [complete_space E] [normed_group F]
  [measurable_space F] [borel_space F]

/-- If `f` is eventually differentiable along a nontrivial filter `l : filter ℝ` that is generated
by convex sets, the norm of `f` tends to infinity along `l`, and `f' = O(g)` along `l`, where `f'`
is the derivative of `f`, then `g` is not integrable on any interval `a..b` such that
`[a, b] ∈ l`. -/
lemma not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_filter {f : ℝ → E} {g : ℝ → F}
  {a b : ℝ} (l : filter ℝ) [ne_bot l] [tendsto_Ixx_class Icc l l] (hl : [a, b] ∈ l)
  (hd : ∀ᶠ x in l, differentiable_at ℝ f x) (hf : tendsto (λ x, ∥f x∥) l at_top)
  (hfg : is_O (deriv f) g l) :
  ¬interval_integrable g volume a b :=
begin
  intro hgi,
  obtain ⟨C, hC₀, s, hsl, hsub, hfd, hg⟩ : ∃ (C : ℝ) (hC₀ : 0 ≤ C) (s ∈ l),
    (∀ (x ∈ s) (y ∈ s), [x, y] ⊆ [a, b]) ∧
    (∀ (x ∈ s) (y ∈ s) (z ∈ [x, y]), differentiable_at ℝ f z) ∧
    (∀ (x ∈ s) (y ∈ s) (z ∈ [x, y]), ∥deriv f z∥ ≤ C * ∥g z∥),
  { rcases hfg.exists_nonneg with ⟨C, C₀, hC⟩,
    have h : ∀ᶠ x : ℝ × ℝ in l.prod l, ∀ y ∈ [x.1, x.2], (differentiable_at ℝ f y ∧
      ∥deriv f y∥ ≤ C * ∥g y∥) ∧ y ∈ [a, b],
      from (tendsto_fst.interval tendsto_snd).eventually ((hd.and hC.bound).and hl).lift'_powerset,
    rcases mem_prod_self_iff.1 h with ⟨s, hsl, hs⟩,
    simp only [prod_subset_iff, mem_set_of_eq] at hs,
    exact ⟨C, C₀, s, hsl, λ x hx y hy z hz, (hs x hx y hy z hz).2,
      λ x hx y hy z hz, (hs x hx y hy z hz).1.1, λ x hx y hy z hz, (hs x hx y hy z hz).1.2⟩ },
  replace hgi : interval_integrable (λ x, C * ∥g x∥) volume a b, by convert hgi.norm.smul C,
  obtain ⟨c, hc, d, hd, hlt⟩ : ∃ (c ∈ s) (d ∈ s), ∥f c∥ + ∫ y in Ι a b, C * ∥g y∥ < ∥f d∥,
  { rcases filter.nonempty_of_mem hsl with ⟨c, hc⟩,
    have : ∀ᶠ x in l, ∥f c∥ + ∫ y in Ι a b, C * ∥g y∥ < ∥f x∥,
      from hf.eventually (eventually_gt_at_top _),
    exact ⟨c, hc, (this.and hsl).exists.imp (λ d hd, ⟨hd.2, hd.1⟩)⟩ },
  specialize hsub c hc d hd, specialize hfd c hc d hd,
  replace hg : ∀ x ∈ Ι c d, ∥deriv f x∥ ≤ C * ∥g x∥, from λ z hz, hg c hc d hd z ⟨hz.1.le, hz.2⟩,
  have hg_ae : ∀ᵐ x ∂(volume.restrict (Ι c d)), ∥deriv f x∥ ≤ C * ∥g x∥,
    from (ae_restrict_mem measurable_set_interval_oc).mono hg,
  have hsub' : Ι c d ⊆ Ι a b,
    from interval_oc_subset_interval_oc_of_interval_subset_interval hsub,
  have hfi : interval_integrable (deriv f) volume c d,
    from (hgi.mono_set hsub).mono_fun' (ae_measurable_deriv _ _) hg_ae,
  refine hlt.not_le (sub_le_iff_le_add'.1 _),
  calc ∥f d∥ - ∥f c∥ ≤ ∥f d - f c∥ : norm_sub_norm_le _ _
  ... = ∥∫ x in c..d, deriv f x∥ : congr_arg _ (integral_deriv_eq_sub hfd hfi).symm
  ... = ∥∫ x in Ι c d, deriv f x∥ : norm_integral_eq_norm_integral_Ioc _
  ... ≤ ∫ x in Ι c d, ∥deriv f x∥ : norm_integral_le_integral_norm _
  ... ≤ ∫ x in Ι c d, C * ∥g x∥ :
    set_integral_mono_on hfi.norm.def (hgi.def.mono_set hsub') measurable_set_interval_oc hg
  ... ≤ ∫ x in Ι a b, C * ∥g x∥ :
    set_integral_mono_set hgi.def (ae_of_all _ $ λ x, mul_nonneg hC₀ (norm_nonneg _))
      hsub'.eventually_le
end

/-- If `a ≠ b`, `c ∈ [a, b]`, `f` is differentiable in the neighborhood of `c` within
`[a, b] \ {c}`, `∥f x∥ → ∞` as `x → c` within `[a, b] \ {c}`, and `f' = O(g)` along
`𝓝[[a, b] \ {c}] c`, where `f'` is the derivative of `f`, then `g` is not interval integrable on
`a..b`. -/
lemma not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_within_diff_singleton
  {f : ℝ → E} {g : ℝ → F} {a b c : ℝ} (hne : a ≠ b) (hc : c ∈ [a, b])
  (h_deriv : ∀ᶠ x in 𝓝[[a, b] \ {c}] c, differentiable_at ℝ f x)
  (h_infty : tendsto (λ x, ∥f x∥) (𝓝[[a, b] \ {c}] c) at_top)
  (hg : is_O (deriv f) g (𝓝[[a, b] \ {c}] c)) :
  ¬interval_integrable g volume a b :=
begin
  obtain ⟨l, hl, hl', hle, hmem⟩ : ∃ l : filter ℝ, tendsto_Ixx_class Icc l l ∧ l.ne_bot ∧
    l ≤ 𝓝 c ∧ [a, b] \ {c} ∈ l,
  { cases (min_lt_max.2 hne).lt_or_lt c with hlt hlt,
    { refine ⟨𝓝[<] c, infer_instance, infer_instance, inf_le_left, _⟩,
      rw ← Iic_diff_right,
      exact diff_mem_nhds_within_diff (Icc_mem_nhds_within_Iic ⟨hlt, hc.2⟩) _ },
    { refine ⟨𝓝[>] c, infer_instance, infer_instance, inf_le_left, _⟩,
      rw ← Ici_diff_left,
      exact diff_mem_nhds_within_diff (Icc_mem_nhds_within_Ici ⟨hc.1, hlt⟩) _ } },
  resetI,
  have : l ≤ 𝓝[[a, b] \ {c}] c, from le_inf hle (le_principal_iff.2 hmem),
  exact not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_filter l
    (mem_of_superset hmem (diff_subset _ _))
    (h_deriv.filter_mono this) (h_infty.mono_left this) (hg.mono this),
end

/-- If `f` is differentiable in a punctured neighborhood of `c`, `∥f x∥ → ∞` as `x → c` (more
formally, along the filter `𝓝[≠] c`), and `f' = O(g)` along `𝓝[≠] c`, where `f'` is the derivative
of `f`, then `g` is not interval integrable on any nontrivial interval `a..b` such that
`c ∈ [a, b]`. -/
lemma not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_punctured {f : ℝ → E} {g : ℝ → F}
  {a b c : ℝ} (h_deriv : ∀ᶠ x in 𝓝[≠] c, differentiable_at ℝ f x)
  (h_infty : tendsto (λ x, ∥f x∥) (𝓝[≠] c) at_top) (hg : is_O (deriv f) g (𝓝[≠] c))
  (hne : a ≠ b) (hc : c ∈ [a, b]) :
  ¬interval_integrable g volume a b :=
have 𝓝[[a, b] \ {c}] c ≤ 𝓝[≠] c, from nhds_within_mono _ (inter_subset_right _ _),
not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_within_diff_singleton hne hc
  (h_deriv.filter_mono this) (h_infty.mono_left this) (hg.mono this)

/-- If `f` grows in the punctured neighborhood of `c : ℝ` at least as fast as `1 / (x - c)`,
then it is not interval integrable on any nontrivial interval `a..b`, `c ∈ [a, b]`. -/
lemma not_interval_integrable_of_sub_inv_is_O_punctured {f : ℝ → F} {a b c : ℝ}
  (hf : is_O (λ x, (x - c)⁻¹) f (𝓝[≠] c)) (hne : a ≠ b) (hc : c ∈ [a, b]) :
  ¬interval_integrable f volume a b :=
begin
  have A : ∀ᶠ x in 𝓝[≠] c, has_deriv_at (λ x, real.log (x - c)) (x - c)⁻¹ x,
  { filter_upwards [self_mem_nhds_within] with x hx,
    simpa using ((has_deriv_at_id x).sub_const c).log (sub_ne_zero.2 hx) },
  have B : tendsto (λ x, ∥real.log (x - c)∥) (𝓝[≠] c) at_top,
  { refine tendsto_abs_at_bot_at_top.comp (real.tendsto_log_nhds_within_zero.comp _),
    rw ← sub_self c,
    exact ((has_deriv_at_id c).sub_const c).tendsto_punctured_nhds one_ne_zero },
  exact not_interval_integrable_of_tendsto_norm_at_top_of_deriv_is_O_punctured
    (A.mono (λ x hx, hx.differentiable_at)) B
    (hf.congr' (A.mono $ λ x hx, hx.deriv.symm) eventually_eq.rfl) hne hc
end

/-- The function `λ x, (x - c)⁻¹` is integrable on `a..b` if and only if `a = b` or `c ∉ [a, b]`. -/
@[simp] lemma interval_integrable_sub_inv_iff {a b c : ℝ} :
  interval_integrable (λ x, (x - c)⁻¹) volume a b ↔ a = b ∨ c ∉ [a, b] :=
begin
  split,
  { refine λ h, or_iff_not_imp_left.2 (λ hne hc, _),
    exact not_interval_integrable_of_sub_inv_is_O_punctured (is_O_refl _ _) hne hc h },
  { rintro (rfl|h₀),
    exacts [interval_integrable.refl,
      interval_integrable_inv (λ x hx, sub_ne_zero.2 $ ne_of_mem_of_not_mem hx h₀)
        (continuous_on_id.sub continuous_on_const)] }
end

/-- The function `λ x, x⁻¹` is integrable on `a..b` if and only if `a = b` or `0 ∉ [a, b]`. -/
@[simp] lemma interval_integrable_inv_iff {a b : ℝ} :
  interval_integrable (λ x, x⁻¹) volume a b ↔ a = b ∨ (0 : ℝ) ∉ [a, b] :=
by simp only [← interval_integrable_sub_inv_iff, sub_zero]