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

import data.finsupp.fintype
import data.int.absolute_value
import data.int.associated
import data.matrix.notation
import data.zmod.quotient
import linear_algebra.free_module.determinant
import linear_algebra.free_module.ideal_quotient
import linear_algebra.free_module.pid
import linear_algebra.isomorphisms
import ring_theory.dedekind_domain.ideal
import ring_theory.norm

/-!

# Ideal norms

This file defines the absolute ideal norm `ideal.abs_norm (I : ideal R) : ℕ` as the cardinality of
the quotient `R ⧸ I` (setting it to 0 if the cardinality is infinite).

## Main definitions

 * `submodule.card_quot (S : submodule R M)`: the cardinality of the quotient `M ⧸ S`, in `ℕ`.
   This maps `⊥` to `0` and `⊤` to `1`.
 * `ideal.abs_norm (I : ideal R)`: the absolute ideal norm, defined as
   the cardinality of the quotient `R ⧸ I`, as a bundled monoid-with-zero homomorphism.

## Main results

 * `map_mul ideal.abs_norm`: multiplicativity of the ideal norm is bundled in
   the definition of `ideal.abs_norm`
 * `ideal.nat_abs_det_basis_change`: the ideal norm is given by the determinant
   of the basis change matrix
 * `ideal.abs_norm_span_singleton`: the ideal norm of a principal ideal is the
   norm of its generator

## TODO

Define the relative norm.
-/

open_locale big_operators

namespace submodule

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

section

/-- The cardinality of `(M ⧸ S)`, if `(M ⧸ S)` is finite, and `0` otherwise.
This is used to define the absolute ideal norm `ideal.abs_norm`.
-/
noncomputable def card_quot (S : submodule R M) : ℕ :=
add_subgroup.index S.to_add_subgroup

@[simp] lemma card_quot_apply (S : submodule R M) [fintype (M ⧸ S)] :
  card_quot S = fintype.card (M ⧸ S) :=
add_subgroup.index_eq_card _

variables (R M)

@[simp] lemma card_quot_bot [infinite M] : card_quot (⊥ : submodule R M) = 0 :=
add_subgroup.index_bot.trans nat.card_eq_zero_of_infinite

@[simp] lemma card_quot_top : card_quot (⊤ : submodule R M) = 1 :=
add_subgroup.index_top

variables {R M}

@[simp] lemma card_quot_eq_one_iff {P : submodule R M} : card_quot P = 1 ↔ P = ⊤ :=
add_subgroup.index_eq_one.trans (by simp [set_like.ext_iff])

end

end submodule

variables {S : Type*} [comm_ring S] [is_domain S]

open submodule

/-- Multiplicity of the ideal norm, for coprime ideals.
This is essentially just a repackaging of the Chinese Remainder Theorem.
-/
lemma card_quot_mul_of_coprime [is_dedekind_domain S] [module.free ℤ S] [module.finite ℤ S]
  {I J : ideal S} (coprime : I ⊔ J = ⊤) : card_quot (I * J) = card_quot I * card_quot J :=
begin
  let b := module.free.choose_basis ℤ S,
  casesI is_empty_or_nonempty (module.free.choose_basis_index ℤ S),
  { haveI : subsingleton S := function.surjective.subsingleton b.repr.to_equiv.symm.surjective,
    nontriviality S,
    exfalso,
    exact not_nontrivial_iff_subsingleton.mpr ‹subsingleton S› ‹nontrivial S› },
  haveI : infinite S := infinite.of_surjective _ b.repr.to_equiv.surjective,
  by_cases hI : I = ⊥,
  { rw [hI, submodule.bot_mul, card_quot_bot, zero_mul] },
  by_cases hJ : J = ⊥,
  { rw [hJ, submodule.mul_bot, card_quot_bot, mul_zero] },
  have hIJ : I * J ≠ ⊥ := mt ideal.mul_eq_bot.mp (not_or hI hJ),

  letI := classical.dec_eq (module.free.choose_basis_index ℤ S),
  letI := I.fintype_quotient_of_free_of_ne_bot hI,
  letI := J.fintype_quotient_of_free_of_ne_bot hJ,
  letI := (I * J).fintype_quotient_of_free_of_ne_bot hIJ,

  rw [card_quot_apply, card_quot_apply, card_quot_apply,
      fintype.card_eq.mpr ⟨(ideal.quotient_mul_equiv_quotient_prod I J coprime).to_equiv⟩,
      fintype.card_prod]
end

/-- If the `d` from `ideal.exists_mul_add_mem_pow_succ` is unique, up to `P`,
then so are the `c`s, up to `P ^ (i + 1)`.
Inspired by [Neukirch], proposition 6.1 -/
lemma ideal.mul_add_mem_pow_succ_inj
  (P : ideal S) {i : ℕ} (a d d' e e' : S) (a_mem : a ∈ P ^ i)
  (e_mem : e ∈ P ^ (i + 1)) (e'_mem : e' ∈ P ^ (i + 1))
  (h : d - d' ∈ P) : (a * d + e) - (a * d' + e') ∈ P ^ (i + 1) :=
begin
  have : a * d - a * d' ∈ P ^ (i + 1),
  { convert ideal.mul_mem_mul a_mem h; simp [mul_sub, pow_succ, mul_comm] },
  convert ideal.add_mem _ this (ideal.sub_mem _ e_mem e'_mem),
  ring,
end

section P_prime

variables {P : ideal S} [P_prime : P.is_prime] (hP : P ≠ ⊥)
include P_prime hP

/-- If `a ∈ P^i \ P^(i+1)` and `c ∈ P^i`, then `a * d + e = c` for `e ∈ P^(i+1)`.
`ideal.mul_add_mem_pow_succ_unique` shows the choice of `d` is unique, up to `P`.
Inspired by [Neukirch], proposition 6.1 -/
lemma ideal.exists_mul_add_mem_pow_succ [is_dedekind_domain S] {i : ℕ}
  (a c : S) (a_mem : a ∈ P ^ i) (a_not_mem : a ∉ P ^ (i + 1)) (c_mem : c ∈ P ^ i) :
  ∃ (d : S) (e ∈ P ^ (i + 1)), a * d + e = c :=
begin
  suffices eq_b : P ^ i = ideal.span {a} ⊔ P ^ (i + 1),
  { rw eq_b at c_mem,
    simp only [mul_comm a],
    exact ideal.mem_span_singleton_sup.mp c_mem },
  refine (ideal.eq_prime_pow_of_succ_lt_of_le hP
    (lt_of_le_of_ne le_sup_right _)
    (sup_le (ideal.span_le.mpr (set.singleton_subset_iff.mpr a_mem))
      (ideal.pow_succ_lt_pow hP i).le)).symm,
  contrapose! a_not_mem with this,
  rw this,
  exact mem_sup.mpr ⟨a, mem_span_singleton_self a, 0, by simp, by simp⟩
end

lemma ideal.mem_prime_of_mul_mem_pow [is_dedekind_domain S]
  {P : ideal S} [P_prime : P.is_prime] (hP : P ≠ ⊥) {i : ℕ}
  {a b : S} (a_not_mem : a ∉ P ^ (i + 1))
  (ab_mem : a * b ∈ P ^ (i + 1)) : b ∈ P :=
begin
  simp only [← ideal.span_singleton_le_iff_mem, ← ideal.dvd_iff_le, pow_succ,
       ← ideal.span_singleton_mul_span_singleton] at a_not_mem ab_mem ⊢,
  exact (prime_pow_succ_dvd_mul (ideal.prime_of_is_prime hP P_prime) ab_mem).resolve_left a_not_mem
end

/-- The choice of `d` in `ideal.exists_mul_add_mem_pow_succ` is unique, up to `P`.
Inspired by [Neukirch], proposition 6.1 -/
lemma ideal.mul_add_mem_pow_succ_unique [is_dedekind_domain S] {i : ℕ}
  (a d d' e e' : S) (a_not_mem : a ∉ P ^ (i + 1))
  (e_mem : e ∈ P ^ (i + 1)) (e'_mem : e' ∈ P ^ (i + 1))
  (h : (a * d + e) - (a * d' + e') ∈ P ^ (i + 1)) : d - d' ∈ P :=
begin
  have : e' - e ∈ P ^ (i + 1) := ideal.sub_mem _ e'_mem e_mem,
  have h' : a * (d - d') ∈ P ^ (i + 1),
  { convert ideal.add_mem _ h (ideal.sub_mem _ e'_mem e_mem),
    ring },
  exact ideal.mem_prime_of_mul_mem_pow hP a_not_mem h'
end

/-- Multiplicity of the ideal norm, for powers of prime ideals. -/
lemma card_quot_pow_of_prime [is_dedekind_domain S] [module.finite ℤ S] [module.free ℤ S] {i : ℕ} :
  card_quot (P ^ i) = card_quot P ^ i :=
begin
  let b := module.free.choose_basis ℤ S,
  classical,
  induction i with i ih,
  { simp },
  letI := ideal.fintype_quotient_of_free_of_ne_bot (P ^ i.succ) (pow_ne_zero _ hP),
  letI := ideal.fintype_quotient_of_free_of_ne_bot (P ^ i) (pow_ne_zero _ hP),
  letI := ideal.fintype_quotient_of_free_of_ne_bot P hP,
  have : P ^ (i + 1) < P ^ i := ideal.pow_succ_lt_pow hP i,
  suffices hquot : map (P ^ i.succ).mkq (P ^ i) ≃ S ⧸ P,
  { rw [pow_succ (card_quot P), ← ih, card_quot_apply (P ^ i.succ),
      ← card_quotient_mul_card_quotient (P ^ i) (P ^ i.succ) this.le,
      card_quot_apply (P ^ i), card_quot_apply P],
    congr' 1,
    rw fintype.card_eq,
    exact ⟨hquot⟩ },
  choose a a_mem a_not_mem using set_like.exists_of_lt this,
  choose f g hg hf using λ c (hc : c ∈ P ^ i),
    ideal.exists_mul_add_mem_pow_succ hP a c a_mem a_not_mem hc,
  choose k hk_mem hk_eq using λ c' (hc' : c' ∈ (map (mkq (P ^ i.succ)) (P ^ i))),
    submodule.mem_map.mp hc',
  refine equiv.of_bijective (λ c', quotient.mk' (f (k c' c'.prop) (hk_mem c' c'.prop))) ⟨_, _⟩,
  { rintros ⟨c₁', hc₁'⟩ ⟨c₂', hc₂'⟩ h,
    rw [subtype.mk_eq_mk, ← hk_eq _ hc₁', ← hk_eq _ hc₂', mkq_apply, mkq_apply,
        submodule.quotient.eq, ← hf _ (hk_mem _ hc₁'), ← hf _ (hk_mem _ hc₂')],
    refine ideal.mul_add_mem_pow_succ_inj _ _ _ _ _ _ a_mem (hg _ _) (hg _ _) _,
    simpa only [submodule.quotient.mk'_eq_mk, submodule.quotient.mk'_eq_mk, submodule.quotient.eq]
      using h, },
  { intros d',
    refine quotient.induction_on' d' (λ d, _),
    have hd' := mem_map.mpr ⟨a * d, ideal.mul_mem_right d _ a_mem, rfl⟩,
    refine ⟨⟨_, hd'⟩, _⟩,
    simp only [submodule.quotient.mk'_eq_mk, ideal.quotient.mk_eq_mk, ideal.quotient.eq,
        subtype.coe_mk],
    refine ideal.mul_add_mem_pow_succ_unique hP a _ _ _ _ a_not_mem
      (hg _ (hk_mem _ hd'))
      (zero_mem _)
      _,
    rw [hf, add_zero],
    exact (submodule.quotient.eq _).mp (hk_eq _ hd') }
end

end P_prime

/-- Multiplicativity of the ideal norm in number rings. -/
theorem card_quot_mul [is_dedekind_domain S] [module.free ℤ S] [module.finite ℤ S] (I J : ideal S) :
  card_quot (I * J) = card_quot I * card_quot J :=
begin
  let b := module.free.choose_basis ℤ S,
  casesI is_empty_or_nonempty (module.free.choose_basis_index ℤ S),
  { haveI : subsingleton S := function.surjective.subsingleton b.repr.to_equiv.symm.surjective,
    nontriviality S,
    exfalso,
    exact not_nontrivial_iff_subsingleton.mpr ‹subsingleton S› ‹nontrivial S›, },
  haveI : infinite S := infinite.of_surjective _ b.repr.to_equiv.surjective,
  exact unique_factorization_monoid.multiplicative_of_coprime card_quot I J
    (card_quot_bot _ _)
    (λ I J hI, by simp [ideal.is_unit_iff.mp hI, ideal.mul_top])
    (λ I i hI, have ideal.is_prime I := ideal.is_prime_of_prime hI,
              by exactI card_quot_pow_of_prime hI.ne_zero)
    (λ I J hIJ, card_quot_mul_of_coprime (ideal.is_unit_iff.mp (hIJ _
      (ideal.dvd_iff_le.mpr le_sup_left)
      (ideal.dvd_iff_le.mpr le_sup_right))))
end

/-- The absolute norm of the ideal `I : ideal R` is the cardinality of the quotient `R ⧸ I`. -/
noncomputable def ideal.abs_norm [infinite S] [is_dedekind_domain S]
  [module.free ℤ S] [module.finite ℤ S] :
  ideal S →*₀ ℕ :=
{ to_fun := submodule.card_quot,
  map_mul' := λ I J, by rw card_quot_mul,
  map_one' := by rw [ideal.one_eq_top, card_quot_top],
  map_zero' := by rw [ideal.zero_eq_bot, card_quot_bot] }

namespace ideal

variables [infinite S] [is_dedekind_domain S] [module.free ℤ S] [module.finite ℤ S]

lemma abs_norm_apply (I : ideal S) : abs_norm I = card_quot I := rfl

@[simp] lemma abs_norm_bot : abs_norm (⊥ : ideal S) = 0 :=
by rw [← ideal.zero_eq_bot, _root_.map_zero]

@[simp] lemma abs_norm_top : abs_norm (⊤ : ideal S) = 1 :=
by rw [← ideal.one_eq_top, _root_.map_one]

@[simp] lemma abs_norm_eq_one_iff {I : ideal S} : abs_norm I = 1 ↔ I = ⊤ :=
by rw [abs_norm_apply, card_quot_eq_one_iff]

/-- Let `e : S ≃ I` be an additive isomorphism (therefore a `ℤ`-linear equiv).
Then an alternative way to compute the norm of `I` is given by taking the determinant of `e`.
See `nat_abs_det_basis_change` for a more familiar formulation of this result. -/
theorem nat_abs_det_equiv (I : ideal S) {E : Type*} [add_equiv_class E S I] (e : E) :
  int.nat_abs (linear_map.det
      ((submodule.subtype I).restrict_scalars ℤ ∘ₗ add_monoid_hom.to_int_linear_map (e : S →+ I))) =
  ideal.abs_norm I :=
begin
  -- `S ⧸ I` might be infinite if `I = ⊥`, but then `e` can't be an equiv.
  by_cases hI : I = ⊥,
  { unfreezingI { subst hI },
    have : (1 : S) ≠ 0 := one_ne_zero,
    have : (1 : S) = 0 := equiv_like.injective e (subsingleton.elim _ _),
    contradiction },

  let ι := module.free.choose_basis_index ℤ S,
  let b := module.free.choose_basis ℤ S,
  casesI is_empty_or_nonempty ι,
  { nontriviality S,
    exact (not_nontrivial_iff_subsingleton.mpr
      (function.surjective.subsingleton b.repr.to_equiv.symm.surjective)
      (by apply_instance)).elim },

  -- Thus `(S ⧸ I)` is isomorphic to a product of `zmod`s, so it is a fintype.
  letI := ideal.fintype_quotient_of_free_of_ne_bot I hI,
  -- Use the Smith normal form to choose a nice basis for `I`.
  letI := classical.dec_eq ι,
  let a := I.smith_coeffs b hI,
  let b' := I.ring_basis b hI,
  let ab := I.self_basis b hI,
  have ab_eq := I.self_basis_def b hI,
  let e' : S ≃ₗ[ℤ] I := b'.equiv ab (equiv.refl _),
  let f : S →ₗ[ℤ] S := (I.subtype.restrict_scalars ℤ).comp (e' : S →ₗ[ℤ] I),
  let f_apply : ∀ x, f x = b'.equiv ab (equiv.refl _) x := λ x, rfl,
  suffices : (linear_map.det f).nat_abs = ideal.abs_norm I,
  { calc  (linear_map.det ((submodule.subtype I).restrict_scalars ℤ ∘ₗ _)).nat_abs
        = (linear_map.det ((submodule.subtype I).restrict_scalars ℤ ∘ₗ
            (↑(add_equiv.to_int_linear_equiv ↑e) : S →ₗ[ℤ] I))).nat_abs : rfl
    ... = (linear_map.det ((submodule.subtype I).restrict_scalars ℤ ∘ₗ _)).nat_abs :
      int.nat_abs_eq_iff_associated.mpr (linear_map.associated_det_comp_equiv _ _ _)
    ... = abs_norm I : this },

  have ha : ∀ i, f (b' i) = a i • b' i,
  { intro i, rw [f_apply, b'.equiv_apply, equiv.refl_apply, ab_eq] },
  have mem_I_iff : ∀ x, x ∈ I ↔ ∀ i, a i ∣ b'.repr x i,
  { intro x, simp_rw [ab.mem_ideal_iff', ab_eq],
    have : ∀ (c : ι → ℤ) i, b'.repr (∑ (j : ι), c j • a j • b' j) i = a i * c i,
    { intros c i,
      simp only [← mul_action.mul_smul, b'.repr_sum_self, mul_comm] },
    split,
    { rintro ⟨c, rfl⟩ i, exact ⟨c i, this c i⟩ },
    { rintros ha,
      choose c hc using ha, exact ⟨c, b'.ext_elem (λ i, trans (hc i) (this c i).symm)⟩ } },

  -- `det f` is equal to `∏ i, a i`,
  letI := classical.dec_eq ι,
  calc  int.nat_abs (linear_map.det f)
      = int.nat_abs (linear_map.to_matrix b' b' f).det : by rw linear_map.det_to_matrix
  ... = int.nat_abs (matrix.diagonal a).det : _
  ... = int.nat_abs (∏ i, a i) : by rw matrix.det_diagonal
  ... = ∏ i, int.nat_abs (a i) : map_prod int.nat_abs_hom a finset.univ
  ... = fintype.card (S ⧸ I) : _
  ... = abs_norm I : (submodule.card_quot_apply _).symm,
  -- since `linear_map.to_matrix b' b' f` is the diagonal matrix with `a` along the diagonal.
  { congr, ext i j,
    rw [linear_map.to_matrix_apply, ha, linear_equiv.map_smul, basis.repr_self, finsupp.smul_single,
        smul_eq_mul, mul_one],
    by_cases h : i = j,
    { rw [h, matrix.diagonal_apply_eq, finsupp.single_eq_same] },
    { rw [matrix.diagonal_apply_ne _ h, finsupp.single_eq_of_ne (ne.symm h)] } },

  -- Now we map everything through the linear equiv `S ≃ₗ (ι → ℤ)`,
  -- which maps `(S ⧸ I)` to `Π i, zmod (a i).nat_abs`.
  haveI : ∀ i, ne_zero ((a i).nat_abs) := λ i,
    ⟨int.nat_abs_ne_zero_of_ne_zero (ideal.smith_coeffs_ne_zero b I hI i)⟩,
  simp_rw [fintype.card_eq.mpr ⟨(ideal.quotient_equiv_pi_zmod I b hI).to_equiv⟩, fintype.card_pi,
           zmod.card] ,
end

/-- Let `b` be a basis for `S` over `ℤ` and `bI` a basis for `I` over `ℤ` of the same dimension.
Then an alternative way to compute the norm of `I` is given by taking the determinant of `bI`
over `b`. -/
theorem nat_abs_det_basis_change {ι : Type*} [fintype ι] [decidable_eq ι]
  (b : basis ι ℤ S) (I : ideal S) (bI : basis ι ℤ I) :
  (b.det (coe ∘ bI)).nat_abs = ideal.abs_norm I :=
begin
  let e := b.equiv bI (equiv.refl _),
  calc (b.det ((submodule.subtype I).restrict_scalars ℤ ∘ bI)).nat_abs
      = (linear_map.det ((submodule.subtype I).restrict_scalars ℤ ∘ₗ (e : S →ₗ[ℤ] I))).nat_abs
    : by rw basis.det_comp_basis
  ... = _ : nat_abs_det_equiv I e
end

@[simp]
lemma abs_norm_span_singleton (r : S) :
  abs_norm (span ({r} : set S)) = (algebra.norm ℤ r).nat_abs :=
begin
  rw algebra.norm_apply,
  by_cases hr : r = 0,
  { simp only [hr, ideal.span_zero, algebra.coe_lmul_eq_mul, eq_self_iff_true, ideal.abs_norm_bot,
      linear_map.det_zero'', set.singleton_zero, _root_.map_zero, int.nat_abs_zero] },
  letI := ideal.fintype_quotient_of_free_of_ne_bot (span {r}) (mt span_singleton_eq_bot.mp hr),
  let b := module.free.choose_basis ℤ S,
  rw [← nat_abs_det_equiv _ (b.equiv (basis_span_singleton b hr) (equiv.refl _))],
  swap, apply_instance,
  congr,
  refine b.ext (λ i, _),
  simp
end

lemma abs_norm_dvd_abs_norm_of_le {I J : ideal S} (h : J ≤ I) : I.abs_norm ∣ J.abs_norm :=
map_dvd abs_norm (dvd_iff_le.mpr h)

@[simp]
lemma abs_norm_span_insert (r : S) (s : set S) :
  abs_norm (span (insert r s)) ∣ gcd (abs_norm (span s)) (algebra.norm ℤ r).nat_abs :=
(dvd_gcd_iff _ _ _).mpr
  ⟨abs_norm_dvd_abs_norm_of_le (span_mono (set.subset_insert _ _)),
  trans
    (abs_norm_dvd_abs_norm_of_le (span_mono (set.singleton_subset_iff.mpr (set.mem_insert _ _))))
    (by rw abs_norm_span_singleton)⟩

lemma irreducible_of_irreducible_abs_norm {I : ideal S} (hI : irreducible I.abs_norm) :
  irreducible I :=
irreducible_iff.mpr
  ⟨λ h, hI.not_unit (by simpa only [ideal.is_unit_iff, nat.is_unit_iff, abs_norm_eq_one_iff]
      using h),
   by rintro a b rfl; simpa only [ideal.is_unit_iff, nat.is_unit_iff, abs_norm_eq_one_iff]
      using hI.is_unit_or_is_unit (_root_.map_mul abs_norm a b)⟩

lemma is_prime_of_irreducible_abs_norm {I : ideal S} (hI : irreducible I.abs_norm) :
  I.is_prime :=
is_prime_of_prime (unique_factorization_monoid.irreducible_iff_prime.mp
  (irreducible_of_irreducible_abs_norm hI))

lemma prime_of_irreducible_abs_norm_span {a : S} (ha : a ≠ 0)
  (hI : irreducible (ideal.span ({a} : set S)).abs_norm) :
  prime a :=
(ideal.span_singleton_prime ha).mp (is_prime_of_irreducible_abs_norm hI)

end ideal


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.mv_polynomial.default
import Mathlib.field_theory.finite.basic
import Mathlib.PostPort

universes u_1 u_2 u_3

namespace Mathlib

/-!
# The Chevalley–Warning theorem

This file contains a proof of the Chevalley–Warning theorem.
Throughout most of this file, `K` denotes a finite field
and `q` is notation for the cardinality of `K`.

## Main results

1. Let `f` be a multivariate polynomial in finitely many variables (`X s`, `s : σ`)
   such that the total degree of `f` is less than `(q-1)` times the cardinality of `σ`.
   Then the evaluation of `f` on all points of `σ → K` (aka `K^σ`) sums to `0`.
   (`sum_mv_polynomial_eq_zero`)
2. The Chevalley–Warning theorem (`char_dvd_card_solutions`).
   Let `f i` be a finite family of multivariate polynomials
   in finitely many variables (`X s`, `s : σ`) such that
   the sum of the total degrees of the `f i` is less than the cardinality of `σ`.
   Then the number of common solutions of the `f i`
   is divisible by the characteristic of `K`.

## Notation

- `K` is a finite field
- `q` is notation for the cardinality of `K`
- `σ` is the indexing type for the variables of a multivariate polynomial ring over `K`

-/

theorem mv_polynomial.sum_mv_polynomial_eq_zero {K : Type u_1} {σ : Type u_2} [fintype K] [field K]
    [fintype σ] [DecidableEq σ] (f : mv_polynomial σ K)
    (h : mv_polynomial.total_degree f < (fintype.card K - 1) * fintype.card σ) :
    (finset.sum finset.univ fun (x : σ → K) => coe_fn (mv_polynomial.eval x) f) = 0 :=
  sorry

/-- The Chevalley–Warning theorem.
Let `(f i)` be a finite family of multivariate polynomials
in finitely many variables (`X s`, `s : σ`) over a finite field of characteristic `p`.
Assume that the sum of the total degrees of the `f i` is less than the cardinality of `σ`.
Then the number of common solutions of the `f i` is divisible by `p`. -/
theorem char_dvd_card_solutions_family {K : Type u_1} {σ : Type u_2} [fintype K] [field K]
    [fintype σ] [DecidableEq K] [DecidableEq σ] (p : ℕ) [char_p K p] {ι : Type u_3} {s : finset ι}
    {f : ι → mv_polynomial σ K}
    (h : (finset.sum s fun (i : ι) => mv_polynomial.total_degree (f i)) < fintype.card σ) :
    p ∣
        fintype.card
          (Subtype fun (x : σ → K) => ∀ (i : ι), i ∈ s → coe_fn (mv_polynomial.eval x) (f i) = 0) :=
  sorry

/-- The Chevalley–Warning theorem.
Let `f` be a multivariate polynomial in finitely many variables (`X s`, `s : σ`)
over a finite field of characteristic `p`.
Assume that the total degree of `f` is less than the cardinality of `σ`.
Then the number of solutions of `f` is divisible by `p`.
See `char_dvd_card_solutions_family` for a version that takes a family of polynomials `f i`. -/
theorem char_dvd_card_solutions {K : Type u_1} {σ : Type u_2} [fintype K] [field K] [fintype σ]
    [DecidableEq K] [DecidableEq σ] (p : ℕ) [char_p K p] {f : mv_polynomial σ K}
    (h : mv_polynomial.total_degree f < fintype.card σ) :
    p ∣ fintype.card (Subtype fun (x : σ → K) => coe_fn (mv_polynomial.eval x) f = 0) :=
  sorry

end Mathlib


import BrownCs22.Library.Tactics

/-

# Welcome to the Lean section of HW1!

Some general guidelines, before we get started.

* When you're doing Lean homework assignments, including this one,
  do *not* edit any of the `import` statements above the
  opening comment. This will most likely break our autograder.

* Speaking of: when you submit this on Gradescope, you'll
  upload it to a separate assignment than the PDF with your other
  solutions. That's because we can autograde Lean, but not normal
  math! In the future, we hope to let you upload both files to the
  same assignment. But autograders are tricky and we haven't
  figured it out yet.

* Your goal in this assignment is to replace the `sorry`s in each
  part with completed proofs. If you can't finish a problem and
  want to submit the assignment half-complete, that's fine --
  but try to "finish" the proof by putting in more `sorry`s.

  For example, if the original problem looks like this:
-/

theorem example_1 (p : Prop) : p → p → p := by
  sorry

/-
  and you work on it, but get stuck at this stage and can't finish:

theorem example_1 (p : Prop) : p → p → p := by
  intro hp1
  intro hp2

  then you should "finish" it by writing `sorry` again:
-/

theorem example_1' (p : Prop) : p → p → p := by
  intro hp1
  intro hp2
  sorry

/-
  Notice that, with the `sorry`, `example_1` is highlighted in yellow.
  Without the sorry, there is a red error message.
  It's safest to submit a file with no red error messages,
  to avoid any unexpected autograder failures.
  The autograder will give you points if all of the warnings and errors are gone.

Let's get started!
-/

variable (p q r s : Prop)

/-

## Problem 1

Fill in the `sorry` in the proof below.
The elimination and introduction rules we talked about in Lecture 4
will be very helpful here!

-/

/- 3 points -/
theorem problem_1 : (p ∧ q ∧ r) → (p ∧ r) := by
  intro hpqr
  eliminate hpqr with hp hqr
  eliminate hqr with hq hr
  split_goal
  { assumption }
  { assumption }

/-

### Notes on the solution

Here are some common questions that have come up!

First of all: the problem is done when there are no red error messages
or yellow warnings.

The hypothesis names `hpqr`, `hq`, `hr`, etc don't matter.
You can use whatever variable names they want here.
There's no connection between the name and the proposition
(I could use the name `hq` for the hypothesis that `r` holds --
it would be confusing, but Lean wouldn't care!)

The `{...}` around `assumption` is optional.
They temporarily "focus" on the first goal.
It's fine if students just write `assumption` twice.
Lean is whitespace sensitive, though, so if they don't use the brackets,
it should be aligned with the other commands.
Even without the brackets, though, there's no way to do the second goal first.

Some of you might try to do `split_goal` before the `eliminate`s.
This is fine! The proof ends up a little longer, but it still works.

If you're struggling with where to start, a good suggestion is
to look over Lecture 4. We saw rules that match different "goal shapes."
One rule in particular matched `→` goals. Which one? ("implication intro").
The key idea is that we choose which rules to use based on the shapes
(the "main connectives") of the goal and/or the hypotheses.
And at the start, there are no hypotheses, so...

-/

/-

## Problem 2

Consider what the theorem below is saying!
"If the truth of `p` implies the falsity of `q`,
 then `p` and `q` cannot both be true."

Does that seem like a reasonable statement?
What do the truth tables of `p → ¬ q` and `¬ (p ∧ q)` look like?
(You do not need to write an answer, but think about it for a moment.
 Try the `#truth_table` command if you want.)

Again, your task is to fill in the `sorry` below to prove this statement.

-/

/- 3 points -/
theorem problem_2 : (p → ¬ q) → ¬ (p ∧ q) := by
  intro hpnq
  intro hpq -- this step is a *proof by contradiction*!
            -- To prove `¬ (p ∧ q)` we assume `p ∧ q` and show a contradiction.
  eliminate hpq with hp hq
  have hnq : ¬ q := hpnq hp  -- this step is "modus ponens"
  contradiction -- at this point, we know both `q` and `¬ q`.

/-

### Notes on the solution

The precise syntax specification of modus ponens is:
* The token `have`
* The name of a new hypothesis to add, here `hnq`
* A single colon `:`
* The proposition to add as the new hypothesis, here `¬ q`.
  This must match the *right hand side* (conclusion) of the implication.
* The token `:=`
* The name of the implication hypothesis, here `hpnq`. Remember that in
  this problem, `hpnq` is the hypothesis that `p → ¬ q`.
* The name of a hypothesis showing the *left hand side* of the implication.
  Here, `hp` is the hypothesis that `p`.

Pay attention to those last two notes especially.
The order of the hypothesis names matters!
First the implication, then the left hand side.
`hpnq hp` works but `hp hpnq` does not.

-/

/-
## Problem 3

This one's a little tricky! Let's reason through it in natural language.

We want to prove that, if we know `((p ∨ q) ∧ (p → r) ∧ (q → s))`,
then we know `r ∨ s`.
So suppose that we know `((p ∨ q) ∧ (p → r) ∧ (q → s))`.
Our goal is to show that `r ∨ s` follows.

From that long statement, we know three facts: `p ∨ q`, `p → r`, and `q → s`.
We'll reason by cases on `p ∨ q`.

First, if we know `p`, then we know `r`, because `p → r`.
And if we know `r` then we know `r ∨ s`.

Second, if we know `q`, then we know `s`, because `q → s`.
And if we know `s` then we know `r ∨ s`.

That completes our proof!

Your task: translate this argument to Lean.

-/

/- 4 points -/
theorem problem_3 : ((p ∨ q) ∧ (p → r) ∧ (q → s)) → (r ∨ s) := by
  intro h_long_and
  eliminate h_long_and with hpq h_short_and
  eliminate h_short_and with hpr hqs
  eliminate hpq with hp hq
  { have hr : r := hpr hp
    left
    assumption }
  { have hs : s := hqs hq
    right
    assumption }

/-

### Notes on the solution

Again, the `{ ... }` are not required.
But if you have the opportunity, try to use them.
It's good practice.

Another good exercise is to try to match up the lines
in the English proof with Lean tactics (`intro`, `eliminate`, `left`, ...)
before writing a full Lean proof.

-/


import GMLInit.Data.Nat.Extended

namespace Nat

section
variable (p : Nat → Bool) (h : ∃ n, p n)
open ENat

def bfind : Nat := toNat (first p) (first.isFinite_of_exists h)

variable {p} {h}

theorem not_prop_of_lt_bfind {x} : (x < bfind p h) → ¬ p x := by
  intro hlt h
  clean unfold bfind at hlt
  apply Nat.not_ge_of_lt hlt
  apply toNat_le_of_isLE
  apply first.isLE_of
  exact h

theorem bfind_le_of_prop {x} : p x → bfind p h ≤ x := by
  intro h
  apply Nat.le_of_not_gt
  intro (hlt:_ < _)
  apply not_prop_of_lt_bfind hlt
  exact h

variable (p) (h)

theorem bfind_prop : p (bfind p h) := by
  have : (first p).isLE (bfind p h) := by
    clean unfold bfind
    rw [isLE_toNat]
  match first.exists_le_of_isLE _ this with
  | ⟨x, hle, hx⟩ =>
    have heq : x = bfind p h := by
      antisymmetry using (.≤.:Nat→Nat→Prop)
      · exact hle
      · exact bfind_le_of_prop hx
      done
    rw [←heq, hx]

end

def find (p : Nat → Prop) [DecidablePred p] (h : ∃ n, p n) : Nat :=
  let p' := λ n => decide (p n)
  let h' : ∃ n, p' n := match h with | ⟨n,hn⟩ => ⟨n, decide_eq_true hn⟩
  bfind p' h'

theorem find_property (p : Nat → Prop) [DecidablePred p] {h : ∃ n, p n} : p (find p h) := by
  apply of_decide_eq_true
  apply bfind_prop (λ n => decide (p n))
  match h with | ⟨n, hn⟩ => exists n; exact decide_eq_true hn

theorem find_minimal (p : Nat → Prop) [DecidablePred p] {h : ∃ n, p n} {x : Nat} : p x → find p h ≤ x := by
  intro hx
  apply bfind_le_of_prop
  · match h with | ⟨n, hn⟩ => exists n; exact decide_eq_true hn
  · exact decide_eq_true hx

theorem find_eq : {p₁ p₂ : Nat → Prop} → [DecidablePred p₁] → [DecidablePred p₂] → p₁ = p₂ → {h₁ : ∃ n, p₁ n} → {h₂ : ∃ n, p₂ n} → Nat.find p₁ h₁ = Nat.find p₂ h₂
| _, _, _, _, rfl, _, _ => congr (congrArg (@find _) (Subsingleton.elim _ _)) (Subsingleton.elim _ _)

theorem find_ext {p₁ p₂ : Nat → Prop} [DecidablePred p₁] [DecidablePred p₂] : (∀ n, p₁ n ↔ p₂ n) → {h₁ : ∃ n, p₁ n} → {h₂ : ∃ n, p₂ n} → Nat.find p₁ h₁ = Nat.find p₂ h₂ :=
  λ h _ _ => find_eq $ by
    funext n
    apply propext
    exact h n

end Nat


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

! This file was ported from Lean 3 source module algebra.order.hom.basic
! leanprover-community/mathlib commit 28aa996fc6fb4317f0083c4e6daf79878d81be33
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/

import Mathlib.Tactic.Positivity
import Mathlib.Data.FunLike.Basic

/-!
# Algebraic order homomorphism classes

This file defines hom classes for common properties at the intersection of order theory and algebra.

## Typeclasses

Basic typeclasses
* `NonnegHomClass`: Homs are nonnegative: `∀ f a, 0 ≤ f a`
* `SubadditiveHomClass`: Homs are subadditive: `∀ f a b, f (a + b) ≤ f a + f b`
* `SubmultiplicativeHomClass`: Homs are submultiplicative: `∀ f a b, f (a * b) ≤ f a * f b`
* `MulLEAddHomClass`: `∀ f a b, f (a * b) ≤ f a + f b`
* `NonarchimedeanHomClass`: `∀ a b, f (a + b) ≤ max (f a) (f b)`

Group norms
* `AddGroupSeminormClass`: Homs are nonnegative, subadditive, even and preserve zero.
* `GroupSeminormClass`: Homs are nonnegative, respect `f (a * b) ≤ f a + f b`, `f a⁻¹ = f a` and
  preserve zero.
* `AddGroupNormClass`: Homs are seminorms such that `f x = 0 → x = 0` for all `x`.
* `GroupNormClass`: Homs are seminorms such that `f x = 0 → x = 1` for all `x`.

Ring norms
* `RingSeminormClass`: Homs are submultiplicative group norms.
* `RingNormClass`: Homs are ring seminorms that are also additive group norms.
* `MulRingSeminormClass`: Homs are ring seminorms that are multiplicative.
* `MulRingNormClass`: Homs are ring norms that are multiplicative.

## Notes

Typeclasses for seminorms are defined here while types of seminorms are defined in
`Analysis.Normed.Group.Seminorm` and `Analysis.Normed.Ring.Seminorm` because absolute values are
multiplicative ring norms but outside of this use we only consider real-valued seminorms.

## TODO

Finitary versions of the current lemmas.
-/

library_note "out-param inheritance"/--
Diamond inheritance cannot depend on `outParam`s in the following circumstances:
 * there are three classes `Top`, `Middle`, `Bottom`
 * all of these classes have a parameter `(α : outParam _)`
 * all of these classes have an instance parameter `[Root α]` that depends on this `outParam`
 * the `Root` class has two child classes: `Left` and `Right`, these are siblings in the hierarchy
 * the instance `Bottom.toMiddle` takes a `[Left α]` parameter
 * the instance `Middle.toTop` takes a `[Right α]` parameter
 * there is a `Leaf` class that inherits from both `Left` and `Right`.
In that case, given instances `Bottom α` and `Leaf α`, Lean cannot synthesize a `Top α` instance,
even though the hypotheses of the instances `Bottom.toMiddle` and `Middle.toTop` are satisfied.

There are two workarounds:
* You could replace the bundled inheritance implemented by the instance `Middle.toTop` with
  unbundled inheritance implemented by adding a `[Top α]` parameter to the `Middle` class. This is
  the preferred option since it is also more compatible with Lean 4, at the cost of being more work
  to implement and more verbose to use.
* You could weaken the `Bottom.toMiddle` instance by making it depend on a subclass of
  `Middle.toTop`'s parameter, in this example replacing `[Left α]` with `[Leaf α]`.
-/

open Function

variable {ι F α β γ δ : Type _}

/-! ### Basics -/

/-- `NonnegHomClass F α β` states that `F` is a type of nonnegative morphisms. -/
class NonnegHomClass (F : Type _) (α β : outParam (Type _)) [Zero β] [LE β] extends
  FunLike F α fun _ => β where
  /-- the image of any element is non negative. -/
  map_nonneg (f : F) : ∀ a, 0 ≤ f a
#align nonneg_hom_class NonnegHomClass

/-- `SubadditiveHomClass F α β` states that `F` is a type of subadditive morphisms. -/
class SubadditiveHomClass (F : Type _) (α β : outParam (Type _)) [Add α] [Add β] [LE β] extends
  FunLike F α fun _ => β where
  /-- the image of a sum is less or equal than the sum of the images. -/
  map_add_le_add (f : F) : ∀ a b, f (a + b) ≤ f a + f b
#align subadditive_hom_class SubadditiveHomClass

/-- `SubmultiplicativeHomClass F α β` states that `F` is a type of submultiplicative morphisms. -/
@[to_additive SubadditiveHomClass]
class SubmultiplicativeHomClass (F : Type _) (α β : outParam (Type _)) [Mul α] [Mul β] [LE β]
  extends FunLike F α fun _ => β where
  /-- the image of a product is less or equal than the product of the images. -/
  map_mul_le_mul (f : F) : ∀ a b, f (a * b) ≤ f a * f b
#align submultiplicative_hom_class SubmultiplicativeHomClass

/-- `MulLEAddHomClass F α β` states that `F` is a type of subadditive morphisms. -/
@[to_additive SubadditiveHomClass]
class MulLEAddHomClass (F : Type _) (α β : outParam (Type _)) [Mul α] [Add β] [LE β]
  extends FunLike F α fun _ => β where
  /-- the image of a product is less or equal than the sum of the images. -/
  map_mul_le_add (f : F) : ∀ a b, f (a * b) ≤ f a + f b
#align mul_le_add_hom_class MulLEAddHomClass

/-- `NonarchimedeanHomClass F α β` states that `F` is a type of non-archimedean morphisms. -/
class NonarchimedeanHomClass (F : Type _) (α β : outParam (Type _)) [Add α] [LinearOrder β]
  extends FunLike F α fun _ => β where
  /-- the image of a sum is less or equal than the maximum of the images. -/
  map_add_le_max (f : F) : ∀ a b, f (a + b) ≤ max (f a) (f b)
#align nonarchimedean_hom_class NonarchimedeanHomClass

export NonnegHomClass (map_nonneg)

export SubadditiveHomClass (map_add_le_add)

export SubmultiplicativeHomClass (map_mul_le_mul)

export MulLEAddHomClass (map_mul_le_add)

export NonarchimedeanHomClass (map_add_le_max)

attribute [simp] map_nonneg

@[to_additive]
theorem le_map_mul_map_div [Group α] [CommSemigroup β] [LE β] [SubmultiplicativeHomClass F α β]
    (f : F) (a b : α) : f a ≤ f b * f (a / b) := by
  simpa only [mul_comm, div_mul_cancel'] using map_mul_le_mul f (a / b) b
#align le_map_mul_map_div le_map_mul_map_div
#align le_map_add_map_sub le_map_add_map_sub

@[to_additive existing]
theorem le_map_add_map_div [Group α] [AddCommSemigroup β] [LE β] [MulLEAddHomClass F α β] (f : F)
    (a b : α) : f a ≤ f b + f (a / b) := by
  simpa only [add_comm, div_mul_cancel'] using map_mul_le_add f (a / b) b
#align le_map_add_map_div le_map_add_map_div
-- #align le_map_add_map_sub le_map_add_map_sub -- Porting note: TODO: `to_additive` clashes

@[to_additive]
theorem le_map_div_mul_map_div [Group α] [CommSemigroup β] [LE β] [SubmultiplicativeHomClass F α β]
    (f : F) (a b c : α) : f (a / c) ≤ f (a / b) * f (b / c) := by
  simpa only [div_mul_div_cancel'] using map_mul_le_mul f (a / b) (b / c)
#align le_map_div_mul_map_div le_map_div_mul_map_div
#align le_map_sub_add_map_sub le_map_sub_add_map_sub

@[to_additive existing]
theorem le_map_div_add_map_div [Group α] [AddCommSemigroup β] [LE β] [MulLEAddHomClass F α β]
    (f : F) (a b c : α) : f (a / c) ≤ f (a / b) + f (b / c) := by
    simpa only [div_mul_div_cancel'] using map_mul_le_add f (a / b) (b / c)
#align le_map_div_add_map_div le_map_div_add_map_div
-- #align le_map_sub_add_map_sub le_map_sub_add_map_sub -- Porting note: TODO: `to_additive` clashes

--namespace Mathlib.Meta.Positivity

--Porting note: tactic extension commented as decided in the weekly porting meeting
-- /-- Extension for the `positivity` tactic: nonnegative maps take nonnegative values. -/
-- @[positivity _ _]
-- unsafe def positivity_map : expr → tactic strictness
--   | expr.app (quote.1 ⇑(%%f)) (quote.1 (%%ₓa)) => nonnegative <$> mk_app `` map_nonneg [f, a]
--   | _ => failed
-- #align tactic.positivity_map tactic.positivity_map

--end Mathlib.Meta.Positivity

/-! ### Group (semi)norms -/

/-- `AddGroupSeminormClass F α` states that `F` is a type of `β`-valued seminorms on the additive
group `α`.

You should extend this class when you extend `AddGroupSeminorm`. -/
class AddGroupSeminormClass (F : Type _) (α β : outParam <| Type _) [AddGroup α]
  [OrderedAddCommMonoid β] extends SubadditiveHomClass F α β where
  /-- The image of zero is zero. -/
  map_zero (f : F) : f 0 = 0
  /-- The map is invariant under negation of its argument. -/
  map_neg_eq_map (f : F) (a : α) : f (-a) = f a
#align add_group_seminorm_class AddGroupSeminormClass

/-- `GroupSeminormClass F α` states that `F` is a type of `β`-valued seminorms on the group `α`.

You should extend this class when you extend `GroupSeminorm`. -/
@[to_additive]
class GroupSeminormClass (F : Type _) (α β : outParam <| Type _) [Group α]
  [OrderedAddCommMonoid β] extends MulLEAddHomClass F α β where
  /-- The image of one is zero. -/
  map_one_eq_zero (f : F) : f 1 = 0
  /-- The map is invariant under inversion of its argument. -/
  map_inv_eq_map (f : F) (a : α) : f a⁻¹ = f a
#align group_seminorm_class GroupSeminormClass

/-- `AddGroupNormClass F α` states that `F` is a type of `β`-valued norms on the additive group
`α`.

You should extend this class when you extend `AddGroupNorm`. -/
class AddGroupNormClass (F : Type _) (α β : outParam <| Type _) [AddGroup α]
  [OrderedAddCommMonoid β] extends AddGroupSeminormClass F α β where
  /-- The argument is zero if its image under the map is zero. -/
  eq_zero_of_map_eq_zero (f : F) {a : α} : f a = 0 → a = 0
#align add_group_norm_class AddGroupNormClass

/-- `GroupNormClass F α` states that `F` is a type of `β`-valued norms on the group `α`.

You should extend this class when you extend `GroupNorm`. -/
@[to_additive]
class GroupNormClass (F : Type _) (α β : outParam <| Type _) [Group α]
  [OrderedAddCommMonoid β] extends GroupSeminormClass F α β where
  /-- The argument is one if its image under the map is zero. -/
  eq_one_of_map_eq_zero (f : F) {a : α} : f a = 0 → a = 1
#align group_norm_class GroupNormClass

export AddGroupSeminormClass (map_neg_eq_map)

export GroupSeminormClass (map_one_eq_zero map_inv_eq_map)

export AddGroupNormClass (eq_zero_of_map_eq_zero)

export GroupNormClass (eq_one_of_map_eq_zero)

attribute [simp] map_one_eq_zero -- porting note: `to_additive` translation already exists

attribute [simp] map_neg_eq_map

attribute [simp] map_inv_eq_map -- porting note: `to_additive` translation already exists

attribute [to_additive] GroupSeminormClass.toMulLEAddHomClass

-- See note [lower instance priority]
instance (priority := 100) AddGroupSeminormClass.toZeroHomClass {_ : AddGroup α}
    {_ : OrderedAddCommMonoid β} [AddGroupSeminormClass F α β] : ZeroHomClass F α β :=
  { ‹AddGroupSeminormClass F α β› with }
#align add_group_seminorm_class.to_zero_hom_class AddGroupSeminormClass.toZeroHomClass

section GroupSeminormClass

variable [Group α] [OrderedAddCommMonoid β] [GroupSeminormClass F α β] (f : F) (x y : α)

@[to_additive]
theorem map_div_le_add : f (x / y) ≤ f x + f y :=
  by
  rw [div_eq_mul_inv, ← map_inv_eq_map f y]
  exact map_mul_le_add _ _ _
#align map_div_le_add map_div_le_add
#align map_sub_le_add map_sub_le_add

@[to_additive]
theorem map_div_rev : f (x / y) = f (y / x) := by rw [← inv_div, map_inv_eq_map]
#align map_div_rev map_div_rev
#align map_sub_rev map_sub_rev

@[to_additive]
theorem le_map_add_map_div' : f x ≤ f y + f (y / x) := by
  simpa only [add_comm, map_div_rev, div_mul_cancel'] using map_mul_le_add f (x / y) y
#align le_map_add_map_div' le_map_add_map_div'
#align le_map_add_map_sub' le_map_add_map_sub'

end GroupSeminormClass

example [OrderedAddCommGroup β] : OrderedAddCommMonoid β :=
  inferInstance

@[to_additive]
theorem abs_sub_map_le_div [Group α] [LinearOrderedAddCommGroup β] [GroupSeminormClass F α β]
    (f : F) (x y : α) : |f x - f y| ≤ f (x / y) :=
  by
  rw [abs_sub_le_iff, sub_le_iff_le_add', sub_le_iff_le_add']
  exact ⟨le_map_add_map_div _ _ _, le_map_add_map_div' _ _ _⟩
#align abs_sub_map_le_div abs_sub_map_le_div
#align abs_sub_map_le_sub abs_sub_map_le_sub

-- See note [lower instance priority]
@[to_additive]
instance (priority := 100) GroupSeminormClass.toNonnegHomClass {_ : Group α}
    {_ : LinearOrderedAddCommMonoid β} [GroupSeminormClass F α β] : NonnegHomClass F α β :=
  { ‹GroupSeminormClass F α β› with
    map_nonneg := fun f a =>
      (nsmul_nonneg_iff two_ne_zero).1 <|
        by
        rw [two_nsmul, ← map_one_eq_zero f, ← div_self' a]
        exact map_div_le_add _ _ _ }
#align group_seminorm_class.to_nonneg_hom_class GroupSeminormClass.toNonnegHomClass
#align add_group_seminorm_class.to_nonneg_hom_class AddGroupSeminormClass.toNonnegHomClass

section GroupNormClass

variable [Group α] [OrderedAddCommMonoid β] [GroupNormClass F α β] (f : F) {x : α}

@[to_additive (attr := simp)]
theorem map_eq_zero_iff_eq_one : f x = 0 ↔ x = 1 :=
  ⟨eq_one_of_map_eq_zero _, by
    rintro rfl
    exact map_one_eq_zero _⟩
#align map_eq_zero_iff_eq_one map_eq_zero_iff_eq_one
#align map_eq_zero_iff_eq_zero map_eq_zero_iff_eq_zero

@[to_additive]
theorem map_ne_zero_iff_ne_one : f x ≠ 0 ↔ x ≠ 1 :=
  (map_eq_zero_iff_eq_one _).not
#align map_ne_zero_iff_ne_one map_ne_zero_iff_ne_one
#align map_ne_zero_iff_ne_zero map_ne_zero_iff_ne_zero

end GroupNormClass

@[to_additive]
theorem map_pos_of_ne_one [Group α] [LinearOrderedAddCommMonoid β] [GroupNormClass F α β] (f : F)
    {x : α} (hx : x ≠ 1) : 0 < f x :=
  (map_nonneg _ _).lt_of_ne <| ((map_ne_zero_iff_ne_one _).2 hx).symm
#align map_pos_of_ne_one map_pos_of_ne_one
#align map_pos_of_ne_zero map_pos_of_ne_zero

/-! ### Ring (semi)norms -/

/-- `RingSeminormClass F α` states that `F` is a type of `β`-valued seminorms on the ring `α`.

You should extend this class when you extend `RingSeminorm`. -/
class RingSeminormClass (F : Type _) (α β : outParam <| Type _) [NonUnitalNonAssocRing α]
  [OrderedSemiring β] extends AddGroupSeminormClass F α β, SubmultiplicativeHomClass F α β
#align ring_seminorm_class RingSeminormClass

/-- `RingNormClass F α` states that `F` is a type of `β`-valued norms on the ring `α`.

You should extend this class when you extend `RingNorm`. -/
class RingNormClass (F : Type _) (α β : outParam <| Type _) [NonUnitalNonAssocRing α]
  [OrderedSemiring β] extends RingSeminormClass F α β, AddGroupNormClass F α β
#align ring_norm_class RingNormClass

/-- `MulRingSeminormClass F α` states that `F` is a type of `β`-valued multiplicative seminorms
on the ring `α`.

You should extend this class when you extend `MulRingSeminorm`. -/
class MulRingSeminormClass (F : Type _) (α β : outParam <| Type _) [NonAssocRing α]
  [OrderedSemiring β] extends AddGroupSeminormClass F α β, MonoidWithZeroHomClass F α β
#align mul_ring_seminorm_class MulRingSeminormClass

/-- `MulRingNormClass F α` states that `F` is a type of `β`-valued multiplicative norms on the
ring `α`.

You should extend this class when you extend `MulRingNorm`. -/
class MulRingNormClass (F : Type _) (α β : outParam <| Type _) [NonAssocRing α]
  [OrderedSemiring β] extends MulRingSeminormClass F α β, AddGroupNormClass F α β
#align mul_ring_norm_class MulRingNormClass

-- See note [out-param inheritance]
-- See note [lower instance priority]
instance (priority := 100) RingSeminormClass.toNonnegHomClass {_ : NonUnitalNonAssocRing α}
    {_ : LinearOrderedSemiring β} [RingSeminormClass F α β] : NonnegHomClass F α β :=
  AddGroupSeminormClass.toNonnegHomClass
#align ring_seminorm_class.to_nonneg_hom_class RingSeminormClass.toNonnegHomClass

-- See note [lower instance priority]
instance (priority := 100) MulRingSeminormClass.toRingSeminormClass {_ : NonAssocRing α}
    {_ : OrderedSemiring β} [MulRingSeminormClass F α β] : RingSeminormClass F α β :=
  { ‹MulRingSeminormClass F α β› with map_mul_le_mul := fun f a b => (map_mul _ _ _).le }
#align mul_ring_seminorm_class.to_ring_seminorm_class MulRingSeminormClass.toRingSeminormClass

-- See note [lower instance priority]
instance (priority := 100) MulRingNormClass.toRingNormClass {_ : NonAssocRing α}
    {_ : OrderedSemiring β} [MulRingNormClass F α β] : RingNormClass F α β :=
  { ‹MulRingNormClass F α β›, MulRingSeminormClass.toRingSeminormClass with }
#align mul_ring_norm_class.to_ring_norm_class MulRingNormClass.toRingNormClass


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

! This file was ported from Lean 3 source module data.pi.interval
! leanprover-community/mathlib commit ad0089aca372256fe53dde13ca0dfea569bf5ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finset.LocallyFinite
import Mathbin.Data.Fintype.BigOperators

/-!
# Intervals in a pi type

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

This file shows that (dependent) functions to locally finite orders equipped with the pointwise
order are locally finite and calculates the cardinality of their intervals.
-/

open Finset Fintype

open BigOperators

variable {ι : Type _} {α : ι → Type _}

namespace Pi

section LocallyFinite

variable [DecidableEq ι] [Fintype ι] [∀ i, DecidableEq (α i)] [∀ i, PartialOrder (α i)]
  [∀ i, LocallyFiniteOrder (α i)]

instance : LocallyFiniteOrder (∀ i, α i) :=
  LocallyFiniteOrder.ofIcc _ (fun a b => piFinset fun i => Icc (a i) (b i)) fun a b x => by
    simp_rw [mem_pi_finset, mem_Icc, le_def, forall_and]

variable (a b : ∀ i, α i)

/- warning: pi.Icc_eq -> Pi.Icc_eq is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{succ (max u1 u2)} (Finset.{max u1 u2} (forall (i : ι), α i)) (Finset.Icc.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrder.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b) (Fintype.piFinset.{u1, u2} ι (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) => α i) (fun (i : ι) => Finset.Icc.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{max (succ u2) (succ u1)} (Finset.{max u2 u1} (forall (i : ι), α i)) (Finset.Icc.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b) (Fintype.piFinset.{u2, u1} ι (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) => α i) (fun (i : ι) => Finset.Icc.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))
Case conversion may be inaccurate. Consider using '#align pi.Icc_eq Pi.Icc_eqₓ'. -/
theorem Icc_eq : Icc a b = piFinset fun i => Icc (a i) (b i) :=
  rfl
#align pi.Icc_eq Pi.Icc_eq

/- warning: pi.card_Icc -> Pi.card_Icc is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Icc.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrder.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Icc.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Icc.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Icc.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i))))
Case conversion may be inaccurate. Consider using '#align pi.card_Icc Pi.card_Iccₓ'. -/
theorem card_Icc : (Icc a b).card = ∏ i, (Icc (a i) (b i)).card :=
  card_piFinset _
#align pi.card_Icc Pi.card_Icc

/- warning: pi.card_Ico -> Pi.card_Ico is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Ico.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrder.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Icc.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Ico.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Icc.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pi.card_Ico Pi.card_Icoₓ'. -/
theorem card_Ico : (Ico a b).card = (∏ i, (Icc (a i) (b i)).card) - 1 := by
  rw [card_Ico_eq_card_Icc_sub_one, card_Icc]
#align pi.card_Ico Pi.card_Ico

/- warning: pi.card_Ioc -> Pi.card_Ioc is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Ioc.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrder.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Icc.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Ioc.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Icc.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pi.card_Ioc Pi.card_Iocₓ'. -/
theorem card_Ioc : (Ioc a b).card = (∏ i, (Icc (a i) (b i)).card) - 1 := by
  rw [card_Ioc_eq_card_Icc_sub_one, card_Icc]
#align pi.card_Ioc Pi.card_Ioc

/- warning: pi.card_Ioo -> Pi.card_Ioo is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Ioo.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrder.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Icc.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrder.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i) (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Ioo.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Icc.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i) (b i)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))
Case conversion may be inaccurate. Consider using '#align pi.card_Ioo Pi.card_Iooₓ'. -/
theorem card_Ioo : (Ioo a b).card = (∏ i, (Icc (a i) (b i)).card) - 2 := by
  rw [card_Ioo_eq_card_Icc_sub_two, card_Icc]
#align pi.card_Ioo Pi.card_Ioo

end LocallyFinite

section Bounded

variable [DecidableEq ι] [Fintype ι] [∀ i, DecidableEq (α i)] [∀ i, PartialOrder (α i)]

section Bot

variable [∀ i, LocallyFiniteOrderBot (α i)] (b : ∀ i, α i)

instance : LocallyFiniteOrderBot (∀ i, α i) :=
  LocallyFiniteOrderTop.ofIic _ (fun b => piFinset fun i => Iic (b i)) fun b x => by
    simp_rw [mem_pi_finset, mem_Iic, le_def]

/- warning: pi.card_Iic -> Pi.card_Iic is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderBot.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Iic.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrderBot.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) b)) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Iic.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (b i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderBot.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Iic.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderBotForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) b)) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Iic.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (b i))))
Case conversion may be inaccurate. Consider using '#align pi.card_Iic Pi.card_Iicₓ'. -/
theorem card_Iic : (Iic b).card = ∏ i, (Iic (b i)).card :=
  card_piFinset _
#align pi.card_Iic Pi.card_Iic

/- warning: pi.card_Iio -> Pi.card_Iio is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderBot.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Iio.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrderBot.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Iic.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (b i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderBot.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (b : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Iio.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderBotForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) b)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Iic.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (b i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pi.card_Iio Pi.card_Iioₓ'. -/
theorem card_Iio : (Iio b).card = (∏ i, (Iic (b i)).card) - 1 := by
  rw [card_Iio_eq_card_Iic_sub_one, card_Iic]
#align pi.card_Iio Pi.card_Iio

end Bot

section Top

variable [∀ i, LocallyFiniteOrderTop (α i)] (a : ∀ i, α i)

instance : LocallyFiniteOrderTop (∀ i, α i) :=
  LocallyFiniteOrderTop.ofIci _ (fun a => piFinset fun i => Ici (a i)) fun a x => by
    simp_rw [mem_pi_finset, mem_Ici, le_def]

/- warning: pi.card_Ici -> Pi.card_Ici is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderTop.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Ici.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrderTop.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a)) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Ici.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderTop.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Ici.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderTopForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a)) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Ici.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i))))
Case conversion may be inaccurate. Consider using '#align pi.card_Ici Pi.card_Iciₓ'. -/
theorem card_Ici : (Ici a).card = ∏ i, (Ici (a i)).card :=
  card_piFinset _
#align pi.card_Ici Pi.card_Ici

/- warning: pi.card_Ioi -> Pi.card_Ioi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} [_inst_1 : DecidableEq.{succ u1} ι] [_inst_2 : Fintype.{u1} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u2} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u2} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderTop.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))] (a : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u1 u2} (forall (i : ι), α i) (Finset.Ioi.{max u1 u2} (forall (i : ι), α i) (Pi.preorder.{u1, u2} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u2} (α i) (_inst_4 i))) (Pi.locallyFiniteOrderTop.{u1, u2} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Finset.prod.{0, u1} Nat ι Nat.commMonoid (Finset.univ.{u1} ι _inst_2) (fun (i : ι) => Finset.card.{u2} (α i) (Finset.Ici.{u2} (α i) (PartialOrder.toPreorder.{u2} (α i) (_inst_4 i)) (_inst_5 i) (a i)))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} [_inst_1 : DecidableEq.{succ u2} ι] [_inst_2 : Fintype.{u2} ι] [_inst_3 : forall (i : ι), DecidableEq.{succ u1} (α i)] [_inst_4 : forall (i : ι), PartialOrder.{u1} (α i)] [_inst_5 : forall (i : ι), LocallyFiniteOrderTop.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))] (a : forall (i : ι), α i), Eq.{1} Nat (Finset.card.{max u2 u1} (forall (i : ι), α i) (Finset.Ioi.{max u2 u1} (forall (i : ι), α i) (Pi.preorder.{u2, u1} ι (fun (i : ι) => α i) (fun (i : ι) => PartialOrder.toPreorder.{u1} (α i) (_inst_4 i))) (Pi.instLocallyFiniteOrderTopForAllPreorderToPreorder.{u2, u1} ι (fun (i : ι) => α i) (fun (a : ι) (b : ι) => _inst_1 a b) _inst_2 (fun (i : ι) (a : α i) (b : α i) => _inst_3 i a b) (fun (i : ι) => _inst_4 i) (fun (i : ι) => _inst_5 i)) a)) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Finset.prod.{0, u2} Nat ι Nat.commMonoid (Finset.univ.{u2} ι _inst_2) (fun (i : ι) => Finset.card.{u1} (α i) (Finset.Ici.{u1} (α i) (PartialOrder.toPreorder.{u1} (α i) (_inst_4 i)) (_inst_5 i) (a i)))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align pi.card_Ioi Pi.card_Ioiₓ'. -/
theorem card_Ioi : (Ioi a).card = (∏ i, (Ici (a i)).card) - 1 := by
  rw [card_Ioi_eq_card_Ici_sub_one, card_Ici]
#align pi.card_Ioi Pi.card_Ioi

end Top

end Bounded

end Pi