/-
Copyright (c) 2022 Jujian Zhang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jujian Zhang, Eric Wieser
-/

import ring_theory.graded_algebra.homogeneous_ideal

/-!

This file contains a proof that the radical of any homogeneous ideal is a homogeneous ideal

## Main statements

* `ideal.is_homogeneous.is_prime_iff`: for any `I : ideal A`, if `I` is homogeneous, then
  `I` is prime if and only if `I` is homogeneously prime, i.e. `I ≠ ⊤` and if `x, y` are
  homogeneous elements such that `x * y ∈ I`, then at least one of `x,y` is in `I`.
* `ideal.is_prime.homogeneous_core`: for any `I : ideal A`, if `I` is prime, then
  `I.homogeneous_core 𝒜` (i.e. the largest homogeneous ideal contained in `I`) is also prime.
* `ideal.is_homogeneous.radical`: for any `I : ideal A`, if `I` is homogeneous, then the
  radical of `I` is homogeneous as well.
* `homogeneous_ideal.radical`: for any `I : homogeneous_ideal 𝒜`, `I.radical` is the the
  radical of `I` as a `homogeneous_ideal 𝒜`

## Implementation details

Throughout this file, the indexing type `ι` of grading is assumed to be a
`linear_ordered_cancel_add_comm_monoid`. This might be stronger than necessary but cancelling
property is strictly necessary; for a counterexample of how `ideal.is_homogeneous.is_prime_iff`
fails for a non-cancellative set see `counterexample/homogeneous_prime_not_prime.lean`.

## Tags

homogeneous, radical
-/

open graded_ring direct_sum set_like finset
open_locale big_operators

variables {ι σ A : Type*}
variables [comm_ring A]
variables [linear_ordered_cancel_add_comm_monoid ι]
variables [set_like σ A] [add_submonoid_class σ A] {𝒜 : ι → σ} [graded_ring 𝒜]
include A

lemma ideal.is_homogeneous.is_prime_of_homogeneous_mem_or_mem
  {I : ideal A} (hI : I.is_homogeneous 𝒜) (I_ne_top : I ≠ ⊤)
  (homogeneous_mem_or_mem : ∀ {x y : A},
    is_homogeneous 𝒜 x → is_homogeneous 𝒜 y → (x * y ∈ I → x ∈ I ∨ y ∈ I)) :
  ideal.is_prime I :=
⟨I_ne_top, begin
  intros x y hxy,
  by_contradiction rid,
  obtain ⟨rid₁, rid₂⟩ := not_or_distrib.mp rid,
  /-
  The idea of the proof is the following :
  since `x * y ∈ I` and `I` homogeneous, then `proj i (x * y) ∈ I` for any `i : ι`.
  Then consider two sets `{i ∈ x.support | xᵢ ∉ I}` and `{j ∈ y.support | yⱼ ∉ J}`;
  let `max₁, max₂` be the maximum of the two sets, then `proj (max₁ + max₂) (x * y) ∈ I`.
  Then, `proj max₁ x ∉ I` and `proj max₂ j ∉ I`
  but `proj i x ∈ I` for all `max₁ < i` and `proj j y ∈ I` for all `max₂ < j`.
  `  proj (max₁ + max₂) (x * y)`
  `= ∑ {(i, j) ∈ supports | i + j = max₁ + max₂}, xᵢ * yⱼ`
  `= proj max₁ x * proj max₂ y`
  `  + ∑ {(i, j) ∈ supports \ {(max₁, max₂)} | i + j = max₁ + max₂}, xᵢ * yⱼ`.
  This is a contradiction, because both `proj (max₁ + max₂) (x * y) ∈ I` and the sum on the
  right hand side is in `I` however `proj max₁ x * proj max₂ y` is not in `I`.
  -/
  classical,
  set set₁ := (decompose 𝒜 x).support.filter (λ i, proj 𝒜 i x ∉ I) with set₁_eq,
  set set₂ := (decompose 𝒜 y).support.filter (λ i, proj 𝒜 i y ∉ I) with set₂_eq,
  have nonempty :
    ∀ (x : A), (x ∉ I) → ((decompose 𝒜 x).support.filter (λ i, proj 𝒜 i x ∉ I)).nonempty,
  { intros x hx,
    rw filter_nonempty_iff,
    contrapose! hx,
    simp_rw proj_apply at hx,
    rw ← sum_support_decompose 𝒜 x,
    exact ideal.sum_mem _ hx, },
  set max₁ := set₁.max' (nonempty x rid₁) with max₁_eq,
  set max₂ := set₂.max' (nonempty y rid₂) with max₂_eq,
  have mem_max₁ : max₁ ∈ set₁ := max'_mem set₁ (nonempty x rid₁),
  have mem_max₂ : max₂ ∈ set₂ := max'_mem set₂ (nonempty y rid₂),
  replace hxy : proj 𝒜 (max₁ + max₂) (x * y) ∈ I := hI _ hxy,

  have mem_I : proj 𝒜 max₁ x * proj 𝒜 max₂ y ∈ I,
  { set antidiag :=
      ((decompose 𝒜 x).support ×ˢ (decompose 𝒜 y).support)
        .filter (λ z : ι × ι, z.1 + z.2 = max₁ + max₂) with ha,
    have mem_antidiag : (max₁, max₂) ∈ antidiag,
    { simp only [add_sum_erase, mem_filter, mem_product],
      exact ⟨⟨mem_of_mem_filter _ mem_max₁, mem_of_mem_filter _ mem_max₂⟩, rfl⟩ },
    have eq_add_sum :=
      calc  proj 𝒜 (max₁ + max₂) (x * y)
          = ∑ ij in antidiag, proj 𝒜 ij.1 x * proj 𝒜 ij.2 y
          : by simp_rw [ha, proj_apply, direct_sum.decompose_mul,
                        direct_sum.coe_mul_apply 𝒜]
      ... = proj 𝒜 max₁ x * proj 𝒜 max₂ y + ∑ ij in antidiag.erase (max₁, max₂),
                                              proj 𝒜 ij.1 x * proj 𝒜 ij.2 y
          : (add_sum_erase _ _ mem_antidiag).symm,
    rw eq_sub_of_add_eq eq_add_sum.symm,
    refine ideal.sub_mem _ hxy (ideal.sum_mem _ (λ z H, _)),
    rcases z with ⟨i, j⟩,
    simp only [mem_erase, prod.mk.inj_iff, ne.def, mem_filter, mem_product] at H,
    rcases H with ⟨H₁, ⟨H₂, H₃⟩, H₄⟩,
    have max_lt : max₁ < i ∨ max₂ < j,
    { rcases lt_trichotomy max₁ i with h | rfl | h,
      { exact or.inl h },
      { refine false.elim (H₁ ⟨rfl, add_left_cancel H₄⟩), },
      { apply or.inr,
        have := add_lt_add_right h j,
        rw H₄ at this,
        exact lt_of_add_lt_add_left this, }, },
    cases max_lt,
    { -- in this case `max₁ < i`, then `xᵢ ∈ I`; for otherwise `i ∈ set₁` then `i ≤ max₁`.
      have not_mem : i ∉ set₁ := λ h, lt_irrefl _
        ((max'_lt_iff set₁ (nonempty x rid₁)).mp max_lt i h),
      rw set₁_eq at not_mem,
      simp only [not_and, not_not, ne.def, mem_filter] at not_mem,
      exact ideal.mul_mem_right _ I (not_mem H₂), },
    { -- in this case  `max₂ < j`, then `yⱼ ∈ I`; for otherwise `j ∈ set₂`, then `j ≤ max₂`.
      have not_mem : j ∉ set₂ := λ h, lt_irrefl _
        ((max'_lt_iff set₂ (nonempty y rid₂)).mp max_lt j h),
      rw set₂_eq at not_mem,
      simp only [not_and, not_not, ne.def, mem_filter] at not_mem,
      exact ideal.mul_mem_left I _ (not_mem H₃), }, },

  have not_mem_I : proj 𝒜 max₁ x * proj 𝒜 max₂ y ∉ I,
  { have neither_mem : proj 𝒜 max₁ x ∉ I ∧ proj 𝒜 max₂ y ∉ I,
    { rw mem_filter at mem_max₁ mem_max₂,
      exact ⟨mem_max₁.2, mem_max₂.2⟩, },
    intro rid,
    cases homogeneous_mem_or_mem ⟨max₁, set_like.coe_mem _⟩ ⟨max₂, set_like.coe_mem _⟩ mem_I,
    { apply neither_mem.1 h },
    { apply neither_mem.2 h }, },

  exact not_mem_I mem_I,
end⟩

lemma ideal.is_homogeneous.is_prime_iff {I : ideal A} (h : I.is_homogeneous 𝒜) :
  I.is_prime ↔
  (I ≠ ⊤) ∧
    ∀ {x y : A}, set_like.is_homogeneous 𝒜 x → set_like.is_homogeneous 𝒜 y
      → (x * y ∈ I → x ∈ I ∨ y ∈ I) :=
⟨λ HI,
  ⟨ne_of_apply_ne _ HI.ne_top, λ x y hx hy hxy, ideal.is_prime.mem_or_mem HI hxy⟩,
  λ ⟨I_ne_top, homogeneous_mem_or_mem⟩,
    h.is_prime_of_homogeneous_mem_or_mem I_ne_top @homogeneous_mem_or_mem⟩

lemma ideal.is_prime.homogeneous_core {I : ideal A} (h : I.is_prime) :
  (I.homogeneous_core 𝒜).to_ideal.is_prime :=
begin
  apply (ideal.homogeneous_core 𝒜 I).is_homogeneous.is_prime_of_homogeneous_mem_or_mem,
  { exact ne_top_of_le_ne_top h.ne_top (ideal.to_ideal_homogeneous_core_le 𝒜 I) },
  rintros x y hx hy hxy,
  have H := h.mem_or_mem (ideal.to_ideal_homogeneous_core_le 𝒜 I hxy),
  refine H.imp _ _,
  { exact ideal.mem_homogeneous_core_of_is_homogeneous_of_mem hx, },
  { exact ideal.mem_homogeneous_core_of_is_homogeneous_of_mem hy, },
end

lemma ideal.is_homogeneous.radical_eq {I : ideal A} (hI : I.is_homogeneous 𝒜) :
  I.radical = Inf { J | J.is_homogeneous 𝒜 ∧ I ≤ J ∧ J.is_prime } :=
begin
  rw ideal.radical_eq_Inf,
  apply le_antisymm,
  { exact Inf_le_Inf (λ J, and.right), },
  { refine Inf_le_Inf_of_forall_exists_le _,
    rintros J ⟨HJ₁, HJ₂⟩,
    refine ⟨(J.homogeneous_core 𝒜).to_ideal, _, J.to_ideal_homogeneous_core_le _⟩,
    refine ⟨homogeneous_ideal.is_homogeneous _, _, HJ₂.homogeneous_core⟩,
    refine hI.to_ideal_homogeneous_core_eq_self.symm.trans_le (ideal.homogeneous_core_mono _ HJ₁), }
end

lemma ideal.is_homogeneous.radical {I : ideal A} (h : I.is_homogeneous 𝒜)  :
  I.radical.is_homogeneous 𝒜 :=
by { rw h.radical_eq, exact ideal.is_homogeneous.Inf (λ _, and.left) }

/-- The radical of a homogenous ideal, as another homogenous ideal. -/
def homogeneous_ideal.radical (I : homogeneous_ideal 𝒜) : homogeneous_ideal 𝒜 :=
⟨I.to_ideal.radical, I.is_homogeneous.radical⟩

@[simp]
lemma homogeneous_ideal.coe_radical (I : homogeneous_ideal 𝒜) :
  I.radical.to_ideal = I.to_ideal.radical := rfl


import .A2_disc
open A2_disc
open is_ring_hom
lemma power_commute_with_morph  {A B :Type}[comm_ring A][comm_ring B](f : A →  B)[is_ring_hom  f](n : ℕ) : ∀ x : A, f( x^n ) = (f x)^n := λ x,
    nat.rec_on n (show  f(x^0) = (f x)^0, {rw [pow_zero x,pow_zero (f x)], exact map_one f,})
        (assume n, assume rec_hyp : f(x^n) = (f(x))^n,
            show f(x^(n+1)) = (f(x))^(n+1),{rw [pow_succ x n,pow_succ (f(x)) n,← rec_hyp],exact map_mul (f)})
structure X (R : Type)[comm_ring R] :=
    (x y : R)
    (inv  : R)
    (certif : (x-y)^2 * inv = 1)
namespace X
    section
        variables {R : Type} [comm_ring R]
        lemma diec_certif : ∀ {ζ1 ζ2 : X R}, (ζ1.x  = ζ2.x) →  (ζ1.y  = ζ2.y) → (ζ1.x - ζ1.y)^2 =  (ζ2.x - ζ2.y)^2 :=
        begin
            intros ζ1 ζ2,
            intros h1 h2,
            rw [h1,h2],
        end
        @[ext] lemma ext  : ∀ {ζ1 ζ2 : X R}, (ζ1.x  = ζ2.x) →  (ζ1.y  = ζ2.y)  → ζ1 = ζ2 := λ ζ1 ζ2,
        begin
            intro hx,
            intro hy,
            cases ζ1,
            cases ζ2,
            congr; try {assumption},
            apply A2_disc.inverse_unique,
            exact ζ1_certif,
            exact ζ2_certif,
            exact diec_certif hx hy,
        end

        def map_X {A B :Type }[comm_ring A][comm_ring B](f : A →  B)[is_ring_hom  f] : X A → X B := λ η,
            begin
                exact {x := f η.x, y := f η.y, inv := f η.inv,certif :=
                begin
                    rw [← map_sub f,← power_commute_with_morph f,← map_mul f,η.certif],
                    exact map_one f,
                end    }
            end
        lemma map_comp_x {A B :Type }[comm_ring A][comm_ring B](f : A →  B)[is_ring_hom  f] (ζ : X (A) ) : (map_X f ζ).x = f ζ.x := rfl
        lemma map_comp_y {A B :Type }[comm_ring A][comm_ring B](f : A →  B)[is_ring_hom  f] (ζ : X (A) ) : (map_X f ζ).y = f ζ.y := rfl
        lemma map_comp_inv {A B :Type }[comm_ring A][comm_ring B](f : A →  B)[is_ring_hom  f] (ζ : X (A) ) :
            (map_X f ζ).inv = f ζ.inv := rfl
        def 𝕏 : CommRing ⥤ Type :=
        { obj := λ R, X R,
        map := λ R R' f, map_X f,
        }
    end
    #print 𝕏
end X


import logic.unique
import data.vector
import data.vector.basic
import data.vector.zip
import fin.additional

/-
The type `vector α n` is implemented as the subtype of `list α` having `length = n`.
Here we just build slightly on the theory from the library including
* 'boxing' and 'unboxing' between length-1 vectors and their values;
* conversions between vectors whose lengths are equal, but not definitionally so;
* lemmas about positive-length vectors in terms of their 'head' and 'tail'.
* 'zipping' two vectors (to make a vector of the product type)
-/

namespace vector

  -- dosen't look like this instance in in the library??
  instance unique {α : Sort*} : unique (vector α 0) :=
  {to_inhabited := {default := vector.nil}, uniq := vector.eq_nil}

  -- would mark @[simp] except I don't want to mess with the standard behaviour
  lemma map_of_of_fn {α : Type*} {β : Type*} {n} (f : fin n → α) (g : α → β) :
  map g (of_fn f) = of_fn (g ∘ f) :=
  begin
    apply ext,
    intro m,
    simp,
  end

  ----------------------------------------------------------------------------------
  section -- 'boxing' and 'unboxing' between length-1 vectors and their values    --
    --------------------------------------------------------------------------------
    parameter {α : Type*}
    variable {v : α}
    variable {x : vector α 1}

    def unbox: (vector α 1) → α :=
    λ x, x.nth 0

    def box: α  → (vector α 1) :=
    λ x, vector.cons x vector.nil

    @[simp] lemma box_unbox: box (unbox x) = x :=
    begin
      rw box,
      rw unbox,
      apply vector.ext,
      rw unique.forall_iff,
      simp,
    end

    @[simp] lemma box_head: box (x.head) = x :=
    begin
      rw box,
      apply vector.ext,
      rw unique.forall_iff,
      simp,
    end

    @[simp] lemma unbox_box: unbox (box v) = v := rfl

    @[simp] lemma head_box: (box v).head = v := rfl

    --------------------------------------------------------------------------------
  end     -- 'boxing' and 'unboxing' between length-1 vectors and their values    --
  ----------------------------------------------------------------------------------

  ----------------------------------------------------------------------------------
  section -- conversions between vectors whose lengths are equal                  --
    --------------------------------------------------------------------------------
    parameter {α : Type*}

    def transcribe {n m : ℕ} (equal : n = m) :
    (vector α n) → (vector α m) :=
    λ vect, ⟨ vect.val, by {simp, exact equal,} ⟩

    -- the following are all about the particular case of converting
    -- between `vector α n` and `vector α n.nat_pred.succ` for `n : N+`
    parameter {n : ℕ+}
    variable {v : vector α n}
    variable {i : fin n}
    variable {v' : vector α n.nat_pred.succ}
    variable {i' : fin n.nat_pred.succ}

    instance to_succ_pred:
    has_coe (vector α n) (vector α n.nat_pred.succ) :=
    { coe := transcribe (eq.symm (pnat.nat_pred_add_one _)) }

    instance from_succ_pred:
    has_coe (vector α n.nat_pred.succ) (vector α n) :=
    { coe := transcribe (pnat.nat_pred_add_one _) }

    lemma nth_push_succ_pred:
    (v' : vector α n).nth i = v'.nth (i : fin n.nat_pred.succ) :=
    begin -- proof is just a lot of rewriting:
      cases i,
      cases v',
      unfold_coes,
      rw transcribe,
      rw fin.transcribe,
      simp,
      rw vector.nth,
      rw vector.nth,
    end

    @[simp] lemma nth_cancel_from_succ_pred:
    (v' : vector α n).nth (i' : fin n) = v'.nth i' :=
    begin -- proof is just a lot of rewriting:
      cases i',
      cases v',
      unfold_coes,
      rw transcribe,
      rw fin.transcribe,
      simp,
      rw vector.nth,
      rw vector.nth,
    end

    @[simp] lemma nth_cancel_to_succ_pred:
    (v : vector α n.nat_pred.succ).nth (i : fin n.nat_pred.succ) = v.nth i :=
    begin -- proof is just a lot of rewriting:
      cases i,
      cases v,
      unfold_coes,
      rw transcribe,
      rw fin.transcribe,
      simp,
      rw vector.nth,
      rw vector.nth,
    end

    --------------------------------------------------------------------------------
  end     -- conversions between vectors whose lengths are equal                  --
  ----------------------------------------------------------------------------------

  ----------------------------------------------------------------------------------
  section -- positive-length vectors in terms of their 'head' and 'tail'          --
    --------------------------------------------------------------------------------
    parameter {α : Type*}
    parameter {n : ℕ}

    lemma holds_for_all_heads_and_tails (p : (vector α n.succ) → Prop) :
    (∀ v, p v) ↔ (∀ x, ∀ (w : vector α n), p (x ::ᵥ w)) :=
    begin
      split,

      -- forward direction
      intros h x w,
      specialize h (x ::ᵥ w),
      exact h,

      -- backward direction
      intros h v,
      specialize h v.head v.tail,
      simp at h,
      exact h,
    end

    lemma holds_for_head_and_tail {x y : α} {xxx yyy : vector α n}
    (φ : α → α → Prop) :
    (∀ i, φ ((x ::ᵥ xxx).nth i) ((y ::ᵥ yyy).nth i)) ↔
    (∀ j, (φ (xxx.nth j) (yyy.nth j))) ∧ φ x y :=
    begin
      split,

      -- LHS → RHS
      assume h,
      split,

      -- LHS → RHS.L
      intro j,
      specialize h j.succ,
      simp at h,
      exact h,

      -- LHS → RHS.R
      specialize h 0,
      simp at h,
      exact h,

      -- LHS → RHS
      intros h j,
      cases fin.eq_zero_or_eq_succ j with j_is_0 j_is_succesor,

      -- LHS → RHS for j=0
      rw j_is_0,
      simp,
      exact h.right,

      -- LHS → RHS for j>0
      cases j_is_succesor with k is_predecessor,
      rw is_predecessor,
      simp,
      exact h.left k,
    end

    -- alternative version in terms of a positive natural
    -- kept for posterity, though I'm not sure this is used
    -- and I think it can be achieved using what is above already
    lemma holds_for_head_and_tail_alt {m : ℕ+} {x y : α} {xxx yyy : vector α m.nat_pred}
    (φ : α → α → Prop) :
    (∀ i, φ (((x ::ᵥ xxx) : vector α m).nth i) (((y ::ᵥ yyy) : vector α m).nth i)) ↔
    (∀ j, (φ (xxx.nth j) (yyy.nth j))) ∧ φ x y :=
    begin
      split,

      -- LHS → RHS
      assume h,
      split,

      -- LHS → RHS.L
      intro j,
      specialize h j.succ,
      simp at h,
      exact h,

      -- LHS → RHS.L
      specialize h 0,
      rw nth_push_succ_pred at h,
      rw nth_push_succ_pred at h,
      simp at h,
      exact h,

      -- LHS → RHS
      intros h j,
      cases @fin.eq_zero_or_eq_succ m.nat_pred j with j_is_0 j_is_succesor,

      -- LHS → RHS for j=0
      change (j : fin (m.nat_pred.succ)) = (0 : fin m) at j_is_0,
      rw (fin.to_succ_pred_injective j_is_0),
      rw nth_push_succ_pred,
      rw nth_push_succ_pred,
      simp,
      exact h.2,

      -- LHS → RHS for j>0
      cases j_is_succesor with i is_predecesor,
      rw nth_push_succ_pred,
      rw nth_push_succ_pred,
      rw is_predecesor,
      simp,
      exact h.1 i,
    end

    --------------------------------------------------------------------------------
  end     -- positive-length vectors in terms of their 'head' and 'tail'          --
  ----------------------------------------------------------------------------------

  ----------------------------------------------------------------------------------
  section -- zip                                                                  --
    --------------------------------------------------------------------------------
    variables {α : Type*} {β : Type*} {n : ℕ} {i : fin n}
    variables {v₁ : vector α n} {v₂ : vector β n}

    -- The library contains the more general `vector.zip_with` but,
    -- unlike as with `list`, has not developed this special case:
    def zip : vector α n → vector β n → vector (α × β) n := zip_with prod.mk

    -- There are many more lemmas that could go here; I'll add them as I need them.

    @[simp] lemma map_fst_zip:
    map prod.fst (zip v₁ v₂) = v₁ :=
    begin
      apply ext,
      intro i,
      rw zip,
      simp,
    end

    @[simp] lemma map_snd_zip:
    map prod.snd (zip v₁ v₂) = v₂ :=
    begin
      apply ext,
      intro i,
      rw zip,
      simp,
    end

    @[simp] lemma nth_zip:
    (zip v₁ v₂).nth i = (v₁.nth i, v₂.nth i) :=
    begin
      rw zip,
      simp,
    end

    --------------------------------------------------------------------------------
  end     -- zip                                                                  --
  ----------------------------------------------------------------------------------
end vector


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro
-/
import order.symm_diff
import order.disjointed
import order.conditionally_complete_lattice
import data.equiv.encodable.lattice
import data.set.countable

/-!
# Measurable spaces and measurable functions

This file defines measurable spaces and measurable functions.

A measurable space is a set equipped with a σ-algebra, a collection of
subsets closed under complementation and countable union. A function
between measurable spaces is measurable if the preimage of each
measurable subset is measurable.

σ-algebras on a fixed set `α` form a complete lattice. Here we order
σ-algebras by writing `m₁ ≤ m₂` if every set which is `m₁`-measurable is
also `m₂`-measurable (that is, `m₁` is a subset of `m₂`). In particular, any
collection of subsets of `α` generates a smallest σ-algebra which
contains all of them.

Do not add measurability lemmas (which could be tagged with
@[measurability]) to this file, since the measurability tactic is downstream
from here. Use `measure_theory.measurable_space` instead.

## References

* <https://en.wikipedia.org/wiki/Measurable_space>
* <https://en.wikipedia.org/wiki/Sigma-algebra>
* <https://en.wikipedia.org/wiki/Dynkin_system>

## Tags

measurable space, σ-algebra, measurable function
-/

open set encodable function equiv
open_locale classical

variables {α β γ δ δ' : Type*} {ι : Sort*} {s t u : set α}

/-- A measurable space is a space equipped with a σ-algebra. -/
structure measurable_space (α : Type*) :=
(measurable_set' : set α → Prop)
(measurable_set_empty : measurable_set' ∅)
(measurable_set_compl : ∀ s, measurable_set' s → measurable_set' sᶜ)
(measurable_set_Union : ∀ f : ℕ → set α, (∀ i, measurable_set' (f i)) → measurable_set' (⋃ i, f i))

attribute [class] measurable_space

instance [h : measurable_space α] : measurable_space (order_dual α) := h

section

/-- `measurable_set s` means that `s` is measurable (in the ambient measure space on `α`) -/
def measurable_set [measurable_space α] : set α → Prop := ‹measurable_space α›.measurable_set'

localized "notation `measurable_set[` m `]` := @measurable_set _ m" in measure_theory

@[simp] lemma measurable_set.empty [measurable_space α] : measurable_set (∅ : set α) :=
‹measurable_space α›.measurable_set_empty

variable {m : measurable_space α}

include m

lemma measurable_set.compl : measurable_set s → measurable_set sᶜ :=
‹measurable_space α›.measurable_set_compl s

lemma measurable_set.of_compl (h : measurable_set sᶜ) : measurable_set s :=
compl_compl s ▸ h.compl

@[simp] lemma measurable_set.compl_iff : measurable_set sᶜ ↔ measurable_set s :=
⟨measurable_set.of_compl, measurable_set.compl⟩

@[simp] lemma measurable_set.univ : measurable_set (univ : set α) :=
by simpa using (@measurable_set.empty α _).compl

@[nontriviality] lemma subsingleton.measurable_set [subsingleton α] {s : set α} :
  measurable_set s :=
subsingleton.set_cases measurable_set.empty measurable_set.univ s

lemma measurable_set.congr {s t : set α} (hs : measurable_set s) (h : s = t) :
  measurable_set t :=
by rwa ← h

lemma measurable_set.bUnion_decode₂ [encodable β] ⦃f : β → set α⦄ (h : ∀ b, measurable_set (f b))
  (n : ℕ) : measurable_set (⋃ b ∈ decode₂ β n, f b) :=
encodable.Union_decode₂_cases measurable_set.empty h

lemma measurable_set.Union [encodable β] ⦃f : β → set α⦄ (h : ∀ b, measurable_set (f b)) :
  measurable_set (⋃ b, f b) :=
begin
  rw ← encodable.Union_decode₂,
  exact ‹measurable_space α›.measurable_set_Union _ (measurable_set.bUnion_decode₂ h)
end

lemma measurable_set.bUnion {f : β → set α} {s : set β} (hs : countable s)
  (h : ∀ b ∈ s, measurable_set (f b)) : measurable_set (⋃ b ∈ s, f b) :=
begin
  rw bUnion_eq_Union,
  haveI := hs.to_encodable,
  exact measurable_set.Union (by simpa using h)
end

lemma set.finite.measurable_set_bUnion {f : β → set α} {s : set β} (hs : finite s)
  (h : ∀ b ∈ s, measurable_set (f b)) :
  measurable_set (⋃ b ∈ s, f b) :=
measurable_set.bUnion hs.countable h

lemma finset.measurable_set_bUnion {f : β → set α} (s : finset β)
  (h : ∀ b ∈ s, measurable_set (f b)) :
  measurable_set (⋃ b ∈ s, f b) :=
s.finite_to_set.measurable_set_bUnion h

lemma measurable_set.sUnion {s : set (set α)} (hs : countable s) (h : ∀ t ∈ s, measurable_set t) :
  measurable_set (⋃₀ s) :=
by { rw sUnion_eq_bUnion, exact measurable_set.bUnion hs h }

lemma set.finite.measurable_set_sUnion {s : set (set α)} (hs : finite s)
  (h : ∀ t ∈ s, measurable_set t) :
  measurable_set (⋃₀ s) :=
measurable_set.sUnion hs.countable h

lemma measurable_set.Union_Prop {p : Prop} {f : p → set α} (hf : ∀ b, measurable_set (f b)) :
  measurable_set (⋃ b, f b) :=
by { by_cases p; simp [h, hf, measurable_set.empty] }

lemma measurable_set.Inter [encodable β] {f : β → set α} (h : ∀ b, measurable_set (f b)) :
  measurable_set (⋂ b, f b) :=
measurable_set.compl_iff.1 $
by { rw compl_Inter, exact measurable_set.Union (λ b, (h b).compl) }

section fintype

local attribute [instance] fintype.encodable

lemma measurable_set.Union_fintype [fintype β] {f : β → set α} (h : ∀ b, measurable_set (f b)) :
  measurable_set (⋃ b, f b) :=
measurable_set.Union h

lemma measurable_set.Inter_fintype [fintype β] {f : β → set α} (h : ∀ b, measurable_set (f b)) :
  measurable_set (⋂ b, f b) :=
measurable_set.Inter h

end fintype

lemma measurable_set.bInter {f : β → set α} {s : set β} (hs : countable s)
  (h : ∀ b ∈ s, measurable_set (f b)) : measurable_set (⋂ b ∈ s, f b) :=
measurable_set.compl_iff.1 $
by { rw compl_Inter₂, exact measurable_set.bUnion hs (λ b hb, (h b hb).compl) }

lemma set.finite.measurable_set_bInter {f : β → set α} {s : set β} (hs : finite s)
  (h : ∀ b ∈ s, measurable_set (f b)) : measurable_set (⋂ b ∈ s, f b) :=
measurable_set.bInter hs.countable h

lemma finset.measurable_set_bInter {f : β → set α} (s : finset β)
  (h : ∀ b ∈ s, measurable_set (f b)) : measurable_set (⋂ b ∈ s, f b) :=
s.finite_to_set.measurable_set_bInter h

lemma measurable_set.sInter {s : set (set α)} (hs : countable s) (h : ∀ t ∈ s, measurable_set t) :
  measurable_set (⋂₀ s) :=
by { rw sInter_eq_bInter, exact measurable_set.bInter hs h }

lemma set.finite.measurable_set_sInter {s : set (set α)} (hs : finite s)
  (h : ∀ t ∈ s, measurable_set t) : measurable_set (⋂₀ s) :=
measurable_set.sInter hs.countable h

lemma measurable_set.Inter_Prop {p : Prop} {f : p → set α} (hf : ∀ b, measurable_set (f b)) :
  measurable_set (⋂ b, f b) :=
by { by_cases p; simp [h, hf, measurable_set.univ] }

@[simp] lemma measurable_set.union {s₁ s₂ : set α} (h₁ : measurable_set s₁)
  (h₂ : measurable_set s₂) :
  measurable_set (s₁ ∪ s₂) :=
by { rw union_eq_Union, exact measurable_set.Union (bool.forall_bool.2 ⟨h₂, h₁⟩) }

@[simp] lemma measurable_set.inter {s₁ s₂ : set α} (h₁ : measurable_set s₁)
  (h₂ : measurable_set s₂) :
  measurable_set (s₁ ∩ s₂) :=
by { rw inter_eq_compl_compl_union_compl, exact (h₁.compl.union h₂.compl).compl }

@[simp] lemma measurable_set.diff {s₁ s₂ : set α} (h₁ : measurable_set s₁)
  (h₂ : measurable_set s₂) :
  measurable_set (s₁ \ s₂) :=
h₁.inter h₂.compl

@[simp] lemma measurable_set.symm_diff {s₁ s₂ : set α}
  (h₁ : measurable_set s₁) (h₂ : measurable_set s₂) :
  measurable_set (s₁ Δ s₂) :=
(h₁.diff h₂).union (h₂.diff h₁)

@[simp] lemma measurable_set.ite {t s₁ s₂ : set α} (ht : measurable_set t) (h₁ : measurable_set s₁)
  (h₂ : measurable_set s₂) :
  measurable_set (t.ite s₁ s₂) :=
(h₁.inter ht).union (h₂.diff ht)

@[simp] lemma measurable_set.cond {s₁ s₂ : set α} (h₁ : measurable_set s₁) (h₂ : measurable_set s₂)
  {i : bool} : measurable_set (cond i s₁ s₂) :=
by { cases i, exacts [h₂, h₁] }

@[simp] lemma measurable_set.disjointed {f : ℕ → set α} (h : ∀ i, measurable_set (f i)) (n) :
  measurable_set (disjointed f n) :=
disjointed_rec (λ t i ht, measurable_set.diff ht $ h _) (h n)

@[simp] lemma measurable_set.const (p : Prop) : measurable_set {a : α | p} :=
by { by_cases p; simp [h, measurable_set.empty]; apply measurable_set.univ }

/-- Every set has a measurable superset. Declare this as local instance as needed. -/
lemma nonempty_measurable_superset (s : set α) : nonempty { t // s ⊆ t ∧ measurable_set t} :=
⟨⟨univ, subset_univ s, measurable_set.univ⟩⟩

end

@[ext] lemma measurable_space.ext : ∀ {m₁ m₂ : measurable_space α},
  (∀ s : set α, m₁.measurable_set' s ↔ m₂.measurable_set' s) → m₁ = m₂
| ⟨s₁, _, _, _⟩ ⟨s₂, _, _, _⟩ h :=
  have s₁ = s₂, from funext $ assume x, propext $ h x,
  by subst this

@[ext] lemma measurable_space.ext_iff {m₁ m₂ : measurable_space α} :
  m₁ = m₂ ↔ (∀ s : set α, m₁.measurable_set' s ↔ m₂.measurable_set' s) :=
⟨by { unfreezingI {rintro rfl}, intro s, refl }, measurable_space.ext⟩

/-- A typeclass mixin for `measurable_space`s such that each singleton is measurable. -/
class measurable_singleton_class (α : Type*) [measurable_space α] : Prop :=
(measurable_set_singleton : ∀ x, measurable_set ({x} : set α))

export measurable_singleton_class (measurable_set_singleton)

attribute [simp] measurable_set_singleton

section measurable_singleton_class

variables [measurable_space α] [measurable_singleton_class α]

lemma measurable_set_eq {a : α} : measurable_set {x | x = a} :=
measurable_set_singleton a

lemma measurable_set.insert {s : set α} (hs : measurable_set s) (a : α) :
  measurable_set (insert a s) :=
(measurable_set_singleton a).union hs

@[simp] lemma measurable_set_insert {a : α} {s : set α} :
  measurable_set (insert a s) ↔ measurable_set s :=
⟨λ h, if ha : a ∈ s then by rwa ← insert_eq_of_mem ha
  else insert_diff_self_of_not_mem ha ▸ h.diff (measurable_set_singleton _),
  λ h, h.insert a⟩

lemma set.subsingleton.measurable_set {s : set α} (hs : s.subsingleton) : measurable_set s :=
hs.induction_on measurable_set.empty measurable_set_singleton

lemma set.finite.measurable_set {s : set α} (hs : finite s) : measurable_set s :=
finite.induction_on hs measurable_set.empty $ λ a s ha hsf hsm, hsm.insert _

protected lemma finset.measurable_set (s : finset α) : measurable_set (↑s : set α) :=
s.finite_to_set.measurable_set

lemma set.countable.measurable_set {s : set α} (hs : countable s) : measurable_set s :=
begin
  rw [← bUnion_of_singleton s],
  exact measurable_set.bUnion hs (λ b hb, measurable_set_singleton b)
end

end measurable_singleton_class

namespace measurable_space

section complete_lattice

instance : has_le (measurable_space α) :=
{ le          := λ m₁ m₂, m₁.measurable_set' ≤ m₂.measurable_set' }

lemma le_def {α} {a b : measurable_space α} :
  a ≤ b ↔ a.measurable_set' ≤ b.measurable_set' := iff.rfl

instance : partial_order (measurable_space α) :=
{ le_refl     := assume a b, le_rfl,
  le_trans    := assume a b c hab hbc, le_def.mpr (le_trans hab hbc),
  le_antisymm := assume a b h₁ h₂, measurable_space.ext $ assume s, ⟨h₁ s, h₂ s⟩,
  ..measurable_space.has_le }

/-- The smallest σ-algebra containing a collection `s` of basic sets -/
inductive generate_measurable (s : set (set α)) : set α → Prop
| basic : ∀ u ∈ s, generate_measurable u
| empty : generate_measurable ∅
| compl : ∀ s, generate_measurable s → generate_measurable sᶜ
| union : ∀ f : ℕ → set α, (∀ n, generate_measurable (f n)) → generate_measurable (⋃ i, f i)

/-- Construct the smallest measure space containing a collection of basic sets -/
def generate_from (s : set (set α)) : measurable_space α :=
{ measurable_set'      := generate_measurable s,
  measurable_set_empty := generate_measurable.empty,
  measurable_set_compl := generate_measurable.compl,
  measurable_set_Union := generate_measurable.union }

lemma measurable_set_generate_from {s : set (set α)} {t : set α} (ht : t ∈ s) :
  (generate_from s).measurable_set' t :=
generate_measurable.basic t ht

lemma generate_from_le {s : set (set α)} {m : measurable_space α}
  (h : ∀ t ∈ s, m.measurable_set' t) : generate_from s ≤ m :=
assume t (ht : generate_measurable s t), ht.rec_on h
  (measurable_set_empty m)
  (assume s _ hs, measurable_set_compl m s hs)
  (assume f _ hf, measurable_set_Union m f hf)

lemma generate_from_le_iff {s : set (set α)} (m : measurable_space α) :
  generate_from s ≤ m ↔ s ⊆ {t | m.measurable_set' t} :=
iff.intro
  (assume h u hu, h _ $ measurable_set_generate_from hu)
  (assume h, generate_from_le h)

@[simp] lemma generate_from_measurable_set [measurable_space α] :
  generate_from {s : set α | measurable_set s} = ‹_› :=
le_antisymm (generate_from_le $ λ _, id) $ λ s, measurable_set_generate_from

/-- If `g` is a collection of subsets of `α` such that the `σ`-algebra generated from `g` contains
the same sets as `g`, then `g` was already a `σ`-algebra. -/
protected def mk_of_closure (g : set (set α)) (hg : {t | (generate_from g).measurable_set' t} = g) :
  measurable_space α :=
{ measurable_set'      := λ s, s ∈ g,
  measurable_set_empty := hg ▸ measurable_set_empty _,
  measurable_set_compl := hg ▸ measurable_set_compl _,
  measurable_set_Union := hg ▸ measurable_set_Union _ }

lemma mk_of_closure_sets {s : set (set α)}
  {hs : {t | (generate_from s).measurable_set' t} = s} :
  measurable_space.mk_of_closure s hs = generate_from s :=
measurable_space.ext $ assume t, show t ∈ s ↔ _, by { conv_lhs { rw [← hs] }, refl }

/-- We get a Galois insertion between `σ`-algebras on `α` and `set (set α)` by using `generate_from`
  on one side and the collection of measurable sets on the other side. -/
def gi_generate_from : galois_insertion (@generate_from α) (λ m, {t | @measurable_set α m t}) :=
{ gc        := assume s, generate_from_le_iff,
  le_l_u    := assume m s, measurable_set_generate_from,
  choice    :=
    λ g hg, measurable_space.mk_of_closure g $ le_antisymm hg $ (generate_from_le_iff _).1 le_rfl,
  choice_eq := assume g hg, mk_of_closure_sets }

instance : complete_lattice (measurable_space α) :=
gi_generate_from.lift_complete_lattice

instance : inhabited (measurable_space α) := ⟨⊤⟩

lemma measurable_set_bot_iff {s : set α} : @measurable_set α ⊥ s ↔ (s = ∅ ∨ s = univ) :=
let b : measurable_space α :=
{ measurable_set'      := λ s, s = ∅ ∨ s = univ,
  measurable_set_empty := or.inl rfl,
  measurable_set_compl := by simp [or_imp_distrib] {contextual := tt},
  measurable_set_Union := assume f hf, classical.by_cases
    (assume h : ∃i, f i = univ,
      let ⟨i, hi⟩ := h in
      or.inr $ eq_univ_of_univ_subset $ hi ▸ le_supr f i)
    (assume h : ¬ ∃i, f i = univ,
      or.inl $ eq_empty_of_subset_empty $ Union_subset $ assume i,
        (hf i).elim (by simp {contextual := tt}) (assume hi, false.elim $ h ⟨i, hi⟩)) } in
have b = ⊥, from bot_unique $ assume s hs,
  hs.elim (λ s, s.symm ▸ @measurable_set_empty _ ⊥) (λ s, s.symm ▸ @measurable_set.univ _ ⊥),
this ▸ iff.rfl

@[simp] theorem measurable_set_top {s : set α} : @measurable_set _ ⊤ s := trivial

@[simp] theorem measurable_set_inf {m₁ m₂ : measurable_space α} {s : set α} :
  @measurable_set _ (m₁ ⊓ m₂) s ↔ @measurable_set _ m₁ s ∧ @measurable_set _ m₂ s :=
iff.rfl

@[simp] theorem measurable_set_Inf {ms : set (measurable_space α)} {s : set α} :
  @measurable_set _ (Inf ms) s ↔ ∀ m ∈ ms, @measurable_set _ m s :=
show s ∈ (⋂₀ _) ↔ _, by simp

@[simp] theorem measurable_set_infi {ι} {m : ι → measurable_space α} {s : set α} :
  @measurable_set _ (infi m) s ↔ ∀ i, @measurable_set _ (m i) s :=
by rw [infi, measurable_set_Inf, forall_range_iff]

theorem measurable_set_sup {m₁ m₂ : measurable_space α} {s : set α} :
  @measurable_set _ (m₁ ⊔ m₂) s ↔ generate_measurable (m₁.measurable_set' ∪ m₂.measurable_set') s :=
iff.refl _

theorem measurable_set_Sup {ms : set (measurable_space α)} {s : set α} :
  @measurable_set _ (Sup ms) s ↔
    generate_measurable {s : set α | ∃ m ∈ ms, @measurable_set _ m s} s :=
begin
  change @measurable_set' _ (generate_from $ ⋃₀ _) _ ↔ _,
  simp [generate_from, ← set_of_exists]
end

theorem measurable_set_supr {ι} {m : ι → measurable_space α} {s : set α} :
  @measurable_set _ (supr m) s ↔
    generate_measurable {s : set α | ∃ i, @measurable_set _ (m i) s} s :=
by simp only [supr, measurable_set_Sup, exists_range_iff]

end complete_lattice

end measurable_space

section measurable_functions
open measurable_space

/-- A function `f` between measurable spaces is measurable if the preimage of every
  measurable set is measurable. -/
def measurable [measurable_space α] [measurable_space β] (f : α → β) : Prop :=
∀ ⦃t : set β⦄, measurable_set t → measurable_set (f ⁻¹' t)

localized "notation `measurable[` m `]` := @measurable _ _ m _" in measure_theory

variables [measurable_space α] [measurable_space β] [measurable_space γ]

lemma measurable_id : measurable (@id α) := λ t, id

lemma measurable_id' : measurable (λ a : α, a) := measurable_id

lemma measurable.comp {α β γ} {mα : measurable_space α} {mβ : measurable_space β}
  {mγ : measurable_space γ} {g : β → γ} {f : α → β} (hg : measurable g) (hf : measurable f) :
  measurable (g ∘ f) :=
λ t ht, hf (hg ht)

@[simp] lemma measurable_const {a : α} : measurable (λ b : β, a) :=
assume s hs, measurable_set.const (a ∈ s)

lemma measurable.le {α} {m m0 : measurable_space α} (hm : m ≤ m0) {f : α → β}
  (hf : measurable[m] f) : measurable[m0] f :=
λ s hs, hm _ (hf hs)

end measurable_functions


import tactic data.finset

namespace finset
variables {α : Type*} {β : Type*} [decidable_eq α]

 def size (A: finset α) := (card A: ℤ)

 lemma union_subset_union_left {s₁ s₂ : finset α} (t) (h : s₁ ⊆ s₂) : s₁ ∪ t ⊆ s₂ ∪ t :=
    begin
        intros x hx ,
        simp,
        simp at hx,
        cases hx,
        left,
        exact h hx,
        right,
        exact hx,
    end

    lemma eq_ss {A B E: finset α} : (A ⊆ E) → (A = B)  → (B ⊆ E) :=
    begin
        finish [finset.subset_iff],
    end

    lemma ss_eq {A E E': finset α} : (A ⊆ E) → (E = E') → (A ⊆ E') :=
    begin
        intros h h',
        rw ← h',
        exact h,
    end

    lemma comp_int (X E: finset α) : (X ∩ (E \ X)) = ∅ := sorry

    lemma card_comp {X E: finset α} : (X ⊆ E) → (card X + card (E \ X) = card E) :=
    begin
      have := finset.card_union_add_card_inter X (E \ X),
      sorry,
    end

    lemma finset_eq {A B: finset α} : (A = B) ↔ ((∀ x ∈ A, x ∈ B) ∧ (∀ x ∈ B, x ∈ A)) :=
    begin
      split,
      intros h,
      rw h,
      split,
      finish,
      finish,
      rintros ⟨h1, h2⟩,
      apply finset.subset.antisymm_iff.mpr,
      split,
      intros a ha,
      exact h1 a ha,
      intros b hb,
      exact h2 b hb,
    end

    lemma em_ss {E : finset α} : ∅ ⊆ E :=
    begin
        exact empty_subset E,
    end

    def inter_subset {A B E: finset α} (hA : A ⊆ E) (hB : B ⊆ E) := subset.trans (inter_subset_left A B) hA

    lemma finset_card (A B : finset α) : (card A: ℤ )  + (card B :ℤ )  = (card (A ∪ B) : ℤ )  + (card (A ∩ B) : ℤ ) :=
    begin
      sorry,
    end

    lemma sdiff_tel {E X Y : finset α} : (X ⊆ Y) → (Y ⊆ E) → (E \ Y ) ∪ (Y \ X) = (E \ X) :=
    begin
      intros hXY hYE,
      sorry,
    end

    lemma size_empt : size (∅ : finset α) = 0 := sorry

    lemma maximal_ss (S: finset α) (C ⊆ powerset S) (hempt: ∅ ∈ C) : ∃ X ∈ C, (∀ Y ∈ powerset S, X ⊂ Y → ¬ Y ∈ C) :=
    begin
      sorry,
    end

    lemma minimal_ss (S: finset α) (C ⊆ powerset S) (h_full : S ∈ C) : ∃ X ∈ C, (∀ Y ∈ powerset S, Y ⊂ X → ¬ Y ∈ C) :=
    begin
      sorry,
    end

    lemma maximal_ss_property (S: finset α){P: finset α → Prop} (hempt: P ∅) : ∃ (X ⊆ S), P X ∧ (∀ Y ⊆ S, X ⊂ Y → ¬ (P Y)) :=
    begin
      sorry,
    end

    lemma minimal_ss_property (S: finset α){P: finset α → Prop} (hfull: P S) : ∃ (X ⊆ S), P X ∧ (∀ Y ⊆ S, Y ⊂ X → ¬ (P Y)) :=
    begin
      sorry,
    end

    lemma extends_to_maximal_property (S: finset α){X₀ : finset α} {P: finset α → Prop} (hempt: P X₀) : ∃ (X ⊆ S), (X₀ ⊆ X) ∧ (P X) ∧ (∀ Y ⊆ S, X ⊂ Y → ¬ (P Y)) :=
    begin
      sorry,
    end

    @[simp] lemma m0 {E: finset α} {e : α} : e ∈ E → {e} ⊆ E := (singleton_subset_iff.symm).mp

    example {E: finset α} {e : α} : e ∈ E → {e} ⊆ E :=
    begin
      simp,
    end

end finset

/-namespace finset
/- For mathlib? -/
lemma inter_of_subset {A B : finset α} (h : A ⊆ B) : A ∩ B = A :=
    sorry

lemma subset_iff_sdiff_eq_empty {x y : finset α} : x ⊆ y ↔ x \ y = ∅ :=
by simp only [sdiff_eq_filter, eq_empty_iff_forall_not_mem, subset_iff, not_and,
  not_not, finset.mem_filter]

lemma empty_sdiff (E : finset α) : E \ ∅ = E :=
    sorry

lemma sdiff_subset (A B : finset α) : A \ B ⊆ A :=
(empty_sdiff A).subst $ sdiff_subset_sdiff (subset.refl A) $ empty_subset B

lemma sdiff_eq_sdiff_inter (A B : finset α) : A \ B = A \ (A ∩ B) :=
by { simp only [ext, not_and, mem_sdiff, mem_inter],
  exact λ a, iff.intro (λ h, ⟨h.1, λ x, h.2⟩) (λ h, ⟨h.1, h.2 h.1⟩) }

lemma card_eq_inter_sdiff (A B : finset α) : card A = card (A ∩ B) + card (A \ B) :=
begin
  have hA : A \ B ∪ A ∩ B = A := by { simp only [ext, mem_union, union_comm, mem_sdiff, mem_inter],
    exact λ a, iff.intro (λ ha, or.elim ha (λ H, H.1) (λ H, H.1))
      (by { intro ha, by_cases h : a ∈ B, { exact or.inl ⟨ha, h⟩ }, { exact or.inr ⟨ha, h⟩ } }) },
  have : disjoint (A \ B) (A ∩ B) := by simp only [disjoint, empty_subset, inf_eq_inter,
    inter_empty, sdiff_inter_self, inter_left_comm, le_iff_subset],
  replace this := card_disjoint_union this, rwa [hA, add_comm] at this
end

lemma card_sdiff_eq (A B : finset α) : card (A \ B) = card A - card (A ∩ B) :=
(nat.sub_eq_of_eq_add $ card_eq_inter_sdiff A B).symm

lemma card_union_inter (A B : finset α) : card A + card B = card (A ∪ B) + card (A ∩ B) :=
begin
  have hBA : card B = card (B \ A) + card (A ∩ B) := inter_comm B A ▸
    (add_comm (card (B ∩ A)) (card (B \ A))) ▸ (card_eq_inter_sdiff B A),
  have Hdis : disjoint A (B \ A) := by simp only [disjoint, empty_subset, inf_eq_inter,
    inter_sdiff_self, le_iff_subset],
  have H : card A + card (B \ A) = card (A ∪ B) :=
    (congr_arg card $ union_sdiff_self_eq_union.symm).substr (card_disjoint_union Hdis).symm,
  calc
  card A + card B = card A + card (B \ A) + card (A ∩ B) : by rw [add_assoc, hBA]
  ... = card (A ∪ B) + card (A ∩ B) : by rw H
end

/- proof by Kenny Lau https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/subject/choosing.20from.20difference.20of.20finsets/near/133624012 -/
lemma exists_sdiff_of_card_lt {x y : finset α} (hcard : card x < card y) : ∃ e : α, e ∈ y \ x :=
suffices ∃ e ∈ y, e ∉ x, by simpa only [exists_prop, finset.mem_sdiff],
by_contradiction $ λ H, not_le_of_lt hcard $ card_le_of_subset $ by simpa only [not_exists,
  exists_prop, not_and, not_not] using H

/- proof by chris hughes
https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/subject/maximal.20finset.20in.20finset.20of.20finsets/near/133905271 -/
lemma max_fun_of_ne_empty {F : finset β} (func : β → ℕ) (h : F ≠ ∅) :
  ∃ x ∈ F, ∀ g ∈ F, func g ≤ func x :=
let ⟨n, hn⟩ := (max_of_ne_empty (mt image_eq_empty.1 h) : ∃ a, a ∈ finset.max (F.image func)) in
let ⟨x, hx₁, hx₂⟩ := mem_image.1 (mem_of_max hn) in
  ⟨x, hx₁, hx₂.symm ▸ λ g hg, le_max_of_mem (mem_image.2 ⟨g, hg, rfl⟩) hn⟩

lemma min_fun_of_ne_empty {F : finset β} (func : β → ℕ) (h : F ≠ ∅) :
  ∃ x ∈ F, ∀ g ∈ F, func x ≤ func g :=
let ⟨n, hn⟩ := (min_of_ne_empty $ mt image_eq_empty.1 h : ∃ a, a ∈ finset.min (F.image func)) in
let ⟨x, hx₁, hx₂⟩ := mem_image.1 (mem_of_min hn) in
  ⟨x, hx₁, hx₂.symm ▸ λ g hg, min_le_of_mem (mem_image.2 ⟨g, hg, rfl⟩) hn⟩

section inst

variables {F : finset α} {Q : finset α → Prop} [decidable_pred Q] {P : α → Prop} [decidable_pred P]

instance decidable_not_forall (c₁ : finset (finset α)) :
  decidable (∃ x : finset α, ¬(x ∈ c₁ → ¬Q x)) :=
decidable_of_iff (∃ x ∈ c₁, Q x) $ by simp only [exists_prop, not_forall_not]

instance decidable_exists_and_mem : decidable (∃ e, e ∈ F ∧ P e) :=
decidable_of_iff (∃ e ∈ F, P e) $ by simp only [exists_prop, not_forall_not]

end inst

end finset

open finset-/
--set_option pp.implicit true -/


/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.algebra.order
import Mathlib.Lean3Lib.init.meta.default

universes u

namespace Mathlib

def min {α : Type u} [linear_order α] (a : α) (b : α) : α := ite (a ≤ b) a b

def max {α : Type u} [linear_order α] (a : α) (b : α) : α := ite (b ≤ a) a b

theorem min_le_left {α : Type u} [linear_order α] (a : α) (b : α) : min a b ≤ a := sorry

theorem min_le_right {α : Type u} [linear_order α] (a : α) (b : α) : min a b ≤ b := sorry

theorem le_min {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : c ≤ a) (h₂ : c ≤ b) :
    c ≤ min a b :=
  sorry

theorem le_max_left {α : Type u} [linear_order α] (a : α) (b : α) : a ≤ max a b := sorry

theorem le_max_right {α : Type u} [linear_order α] (a : α) (b : α) : b ≤ max a b := sorry

theorem max_le {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : a ≤ c) (h₂ : b ≤ c) :
    max a b ≤ c :=
  sorry

theorem eq_min {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : c ≤ a) (h₂ : c ≤ b)
    (h₃ : ∀ {d : α}, d ≤ a → d ≤ b → d ≤ c) : c = min a b :=
  le_antisymm (le_min h₁ h₂) (h₃ (min_le_left a b) (min_le_right a b))

theorem min_comm {α : Type u} [linear_order α] (a : α) (b : α) : min a b = min b a :=
  eq_min (min_le_right a b) (min_le_left a b) fun (c : α) (h₁ : c ≤ b) (h₂ : c ≤ a) => le_min h₂ h₁

theorem min_assoc {α : Type u} [linear_order α] (a : α) (b : α) (c : α) :
    min (min a b) c = min a (min b c) :=
  sorry

theorem min_left_comm {α : Type u} [linear_order α] (a : α) (b : α) (c : α) :
    min a (min b c) = min b (min a c) :=
  left_comm min min_comm min_assoc

@[simp] theorem min_self {α : Type u} [linear_order α] (a : α) : min a a = a := sorry

theorem min_eq_left {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) : min a b = a :=
  Eq.symm (eq_min (le_refl a) h fun (d : α) (ᾰ : d ≤ a) (ᾰ_1 : d ≤ b) => ᾰ)

theorem min_eq_right {α : Type u} [linear_order α] {a : α} {b : α} (h : b ≤ a) : min a b = b :=
  min_comm b a ▸ min_eq_left h

theorem eq_max {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : a ≤ c) (h₂ : b ≤ c)
    (h₃ : ∀ {d : α}, a ≤ d → b ≤ d → c ≤ d) : c = max a b :=
  le_antisymm (h₃ (le_max_left a b) (le_max_right a b)) (max_le h₁ h₂)

theorem max_comm {α : Type u} [linear_order α] (a : α) (b : α) : max a b = max b a :=
  eq_max (le_max_right a b) (le_max_left a b) fun (c : α) (h₁ : b ≤ c) (h₂ : a ≤ c) => max_le h₂ h₁

theorem max_assoc {α : Type u} [linear_order α] (a : α) (b : α) (c : α) :
    max (max a b) c = max a (max b c) :=
  sorry

theorem max_left_comm {α : Type u} [linear_order α] (a : α) (b : α) (c : α) :
    max a (max b c) = max b (max a c) :=
  left_comm max max_comm max_assoc

@[simp] theorem max_self {α : Type u} [linear_order α] (a : α) : max a a = a := sorry

theorem max_eq_left {α : Type u} [linear_order α] {a : α} {b : α} (h : b ≤ a) : max a b = a :=
  Eq.symm (eq_max (le_refl a) h fun (d : α) (ᾰ : a ≤ d) (ᾰ_1 : b ≤ d) => ᾰ)

theorem max_eq_right {α : Type u} [linear_order α] {a : α} {b : α} (h : a ≤ b) : max a b = b :=
  max_comm b a ▸ max_eq_left h

/- these rely on lt_of_lt -/

theorem min_eq_left_of_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) : min a b = a :=
  min_eq_left (le_of_lt h)

theorem min_eq_right_of_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : b < a) :
    min a b = b :=
  min_eq_right (le_of_lt h)

theorem max_eq_left_of_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : b < a) : max a b = a :=
  max_eq_left (le_of_lt h)

theorem max_eq_right_of_lt {α : Type u} [linear_order α] {a : α} {b : α} (h : a < b) :
    max a b = b :=
  max_eq_right (le_of_lt h)

/- these use the fact that it is a linear ordering -/

theorem lt_min {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : a < b) (h₂ : a < c) :
    a < min b c :=
  sorry

theorem max_lt {α : Type u} [linear_order α] {a : α} {b : α} {c : α} (h₁ : a < c) (h₂ : b < c) :
    max a b < c :=
  sorry

end Mathlib


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

import topology.algebra.valuation
import topology.algebra.with_zero_topology
import topology.algebra.uniform_field

/-!
# Valued fields and their completions

In this file we study the topology of a field `K` endowed with a valuation (in our application
to adic spaces, `K` will be the valuation field associated to some valuation on a ring, defined in
valuation.basic).

We already know from valuation.topology that one can build a topology on `K` which
makes it a topological ring.

The first goal is to show `K` is a topological *field*, ie inversion is continuous
at every non-zero element.

The next goal is to prove `K` is a *completable* topological field. This gives us
a completion `hat K` which is a topological field. We also prove that `K` is automatically
separated, so the map from `K` to `hat K` is injective.

Then we extend the valuation given on `K` to a valuation on `hat K`.
-/

open filter set
open_locale topological_space

section division_ring

variables {K : Type*} [division_ring K]

section valuation_topological_division_ring

section inversion_estimate
variables {Γ₀ : Type*} [linear_ordered_comm_group_with_zero Γ₀] (v : valuation K Γ₀)

-- The following is the main technical lemma ensuring that inversion is continuous
-- in the topology induced by a valuation on a division ring (ie the next instance)
-- and the fact that a valued field is completable
-- [BouAC, VI.5.1 Lemme 1]
lemma valuation.inversion_estimate {x y : K} {γ : Γ₀ˣ} (y_ne : y ≠ 0)
  (h : v (x - y) < min (γ * ((v y) * (v y))) (v y)) :
  v (x⁻¹ - y⁻¹) < γ :=
begin
  have hyp1 : v (x - y) < γ * ((v y) * (v y)),
    from lt_of_lt_of_le h (min_le_left _ _),
  have hyp1' : v (x - y) * ((v y) * (v y))⁻¹ < γ,
    from mul_inv_lt_of_lt_mul₀ hyp1,
  have hyp2 : v (x - y) < v y,
    from lt_of_lt_of_le h (min_le_right _ _),
  have key : v x = v y, from valuation.map_eq_of_sub_lt v hyp2,
  have x_ne : x ≠ 0,
  { intro h,
    apply y_ne,
    rw [h, v.map_zero] at key,
    exact v.zero_iff.1 key.symm },
  have decomp : x⁻¹ - y⁻¹ = x⁻¹ * (y - x) * y⁻¹,
  by rw [mul_sub_left_distrib, sub_mul, mul_assoc,
        show y * y⁻¹ = 1, from mul_inv_cancel y_ne,
        show x⁻¹ * x = 1, from inv_mul_cancel x_ne, mul_one, one_mul],
  calc
    v (x⁻¹ - y⁻¹) = v (x⁻¹ * (y - x) * y⁻¹) : by rw decomp
    ... = (v x⁻¹) * (v $ y - x) * (v y⁻¹) : by repeat { rw valuation.map_mul }
    ... = (v x)⁻¹ * (v $ y - x) * (v y)⁻¹ : by rw [v.map_inv, v.map_inv]
    ... = (v $ y - x) * ((v y) * (v y))⁻¹ : by
      { rw [mul_assoc, mul_comm, key, mul_assoc, mul_inv_rev₀] }
    ... = (v $ y - x) * ((v y) * (v y))⁻¹ : rfl
    ... = (v $ x - y) * ((v y) * (v y))⁻¹ : by rw valuation.map_sub_swap
    ... < γ : hyp1',
end
end inversion_estimate

open valued

/-- The topology coming from a valuation on a division ring makes it a topological division ring
    [BouAC, VI.5.1 middle of Proposition 1] -/
@[priority 100]
instance valued.topological_division_ring [valued K] : topological_division_ring K :=
{ continuous_inv :=
    begin
      intros x x_ne s s_in,
      cases valued.mem_nhds.mp s_in with γ hs, clear s_in,
      rw [mem_map, valued.mem_nhds],
      change ∃ (γ : (valued.Γ₀ K)ˣ), {y : K | v (y - x) < γ} ⊆ {x : K | x⁻¹ ∈ s},
      have vx_ne := (valuation.ne_zero_iff $ v).mpr x_ne,
      let γ' := units.mk0 _ vx_ne,
      use min (γ * (γ'*γ')) γ',
      intros y y_in,
      apply hs,
      simp only [mem_set_of_eq] at y_in,
      rw [units.min_coe, units.coe_mul, units.coe_mul] at y_in,
      exact valuation.inversion_estimate _ x_ne y_in
    end,
  ..(by apply_instance : topological_ring K) }

/-- A valued division ring is separated. -/
@[priority 100]
instance valued_ring.separated [valued K] : separated_space K :=
begin
  apply topological_add_group.separated_of_zero_sep,
  intros x x_ne,
  refine ⟨{k | v k < v x}, _, λ h, lt_irrefl _ h⟩,
  rw valued.mem_nhds,
  have vx_ne := (valuation.ne_zero_iff $ v).mpr x_ne,
  let γ' := units.mk0 _ vx_ne,
  exact ⟨γ', λ y hy, by simpa using hy⟩,
end

section
local attribute [instance] linear_ordered_comm_group_with_zero.topological_space

open valued

lemma valued.continuous_valuation [valued K] : continuous (v : K → Γ₀ K) :=
begin
  rw continuous_iff_continuous_at,
  intro x,
  classical,
  by_cases h : x = 0,
  { rw h,
    change tendsto _ _ (𝓝 (v (0 : K))),
    erw valuation.map_zero,
    rw linear_ordered_comm_group_with_zero.tendsto_zero,
    intro γ,
    rw valued.mem_nhds_zero,
    use [γ, set.subset.refl _] },
  { change tendsto _ _ _,
    have v_ne : v x ≠ 0, from (valuation.ne_zero_iff _).mpr h,
    rw linear_ordered_comm_group_with_zero.tendsto_of_ne_zero v_ne,
    apply valued.loc_const v_ne },
end
end

end valuation_topological_division_ring

end division_ring

section valuation_on_valued_field_completion
open uniform_space

variables {K : Type*} [field K] [valued K]

open valued uniform_space

local notation `hat ` := completion

/-- A valued field is completable. -/
@[priority 100]
instance valued.completable : completable_top_field K :=
{ nice := begin
    rintros F hF h0,
    have : ∃ (γ₀ : (Γ₀ K)ˣ) (M ∈ F), ∀ x ∈ M, (γ₀ : Γ₀ K) ≤ v x,
    { rcases filter.inf_eq_bot_iff.mp h0 with ⟨U, U_in, M, M_in, H⟩,
      rcases valued.mem_nhds_zero.mp U_in with ⟨γ₀, hU⟩,
      existsi [γ₀, M, M_in],
      intros x xM,
      apply le_of_not_lt _,
      intro hyp,
      have : x ∈ U ∩ M := ⟨hU hyp, xM⟩,
      rwa H at this },
    rcases this with ⟨γ₀, M₀, M₀_in, H₀⟩,
    rw valued.cauchy_iff at hF ⊢,
    refine ⟨hF.1.map _, _⟩,
    replace hF := hF.2,
    intros γ,
    rcases hF (min (γ * γ₀ * γ₀) γ₀) with ⟨M₁, M₁_in, H₁⟩, clear hF,
    use (λ x : K, x⁻¹) '' (M₀ ∩ M₁),
    split,
    { rw mem_map,
      apply mem_of_superset (filter.inter_mem M₀_in M₁_in),
      exact subset_preimage_image _ _ },
    { rintros _ ⟨x, ⟨x_in₀, x_in₁⟩, rfl⟩ _ ⟨y, ⟨y_in₀, y_in₁⟩, rfl⟩,
      simp only [mem_set_of_eq],
      specialize H₁ x x_in₁ y y_in₁,
      replace x_in₀ := H₀ x x_in₀,
      replace y_in₀ := H₀ y y_in₀, clear H₀,
      apply valuation.inversion_estimate,
      { have : v x ≠ 0,
        { intro h, rw h at x_in₀, simpa using x_in₀, },
        exact (valuation.ne_zero_iff _).mp this },
      { refine lt_of_lt_of_le H₁ _,
        rw units.min_coe,
        apply min_le_min _ x_in₀,
        rw mul_assoc,
        have : ((γ₀ * γ₀ : (Γ₀ K)ˣ) : Γ₀ K) ≤ v x * v x,
          from calc ↑γ₀ * ↑γ₀ ≤ ↑γ₀ * v x : mul_le_mul_left' x_in₀ ↑γ₀
                          ... ≤ _ : mul_le_mul_right' x_in₀ (v x),
        rw units.coe_mul,
        exact mul_le_mul_left' this γ } }
  end,
  ..valued_ring.separated }

local attribute [instance] linear_ordered_comm_group_with_zero.topological_space

/-- The extension of the valuation of a valued field to the completion of the field. -/
noncomputable def valued.extension : hat K → Γ₀ K :=
completion.dense_inducing_coe.extend (v : K → Γ₀ K)

lemma valued.continuous_extension : continuous (valued.extension : hat K → Γ₀ K) :=
 begin
  refine completion.dense_inducing_coe.continuous_extend _,
  intro x₀,
  by_cases h : x₀ = coe 0,
  { refine ⟨0, _⟩,
    erw [h, ← completion.dense_inducing_coe.to_inducing.nhds_eq_comap]; try { apply_instance },
    rw linear_ordered_comm_group_with_zero.tendsto_zero,
    intro γ₀,
    rw valued.mem_nhds,
    exact ⟨γ₀, by simp⟩ },
  { have preimage_one : v ⁻¹' {(1 : Γ₀ K)} ∈ 𝓝 (1 : K),
    { have : v (1 : K) ≠ 0, { rw valuation.map_one, exact zero_ne_one.symm },
      convert valued.loc_const this,
      ext x,
      rw [valuation.map_one, mem_preimage, mem_singleton_iff, mem_set_of_eq] },
    obtain ⟨V, V_in, hV⟩ : ∃ V ∈ 𝓝 (1 : hat K), ∀ x : K, (x : hat K) ∈ V → v x = 1,
    { rwa [completion.dense_inducing_coe.nhds_eq_comap, mem_comap] at preimage_one },

    have : ∃ V' ∈ (𝓝 (1 : hat K)), (0 : hat K) ∉ V' ∧ ∀ x y ∈ V', x*y⁻¹ ∈ V,
    { have : tendsto (λ p : hat K × hat K, p.1*p.2⁻¹) ((𝓝 1).prod (𝓝 1)) (𝓝 1),
      { rw ← nhds_prod_eq,
        conv {congr, skip, skip, rw ← (one_mul (1 : hat K))},
        refine tendsto.mul continuous_fst.continuous_at
                           (tendsto.comp _ continuous_snd.continuous_at),
        convert topological_division_ring.continuous_inv (1 : hat K) zero_ne_one.symm,
        exact inv_one.symm },
      rcases tendsto_prod_self_iff.mp this V V_in with ⟨U, U_in, hU⟩,
      let hatKstar := ({0}ᶜ : set $ hat K),
      have : hatKstar ∈ 𝓝 (1 : hat K),
      from compl_singleton_mem_nhds zero_ne_one.symm,
      use  [U ∩ hatKstar, filter.inter_mem U_in this],
      split,
      { rintro ⟨h, h'⟩,
        rw mem_compl_singleton_iff at h',
        exact h' rfl },
      { rintros x ⟨hx, _⟩ y ⟨hy, _⟩,
        apply hU ; assumption } },
    rcases this with ⟨V', V'_in, zeroV', hV'⟩,
    have nhds_right : (λ x, x*x₀) '' V' ∈ 𝓝 x₀,
    { have l : function.left_inverse (λ x : hat K, x * x₀⁻¹) (λ x : hat K, x * x₀),
      { intro x,
        simp only [mul_assoc, mul_inv_cancel h, mul_one] },
      have r: function.right_inverse (λ x : hat K, x * x₀⁻¹) (λ x : hat K, x * x₀),
      { intro x,
        simp only [mul_assoc, inv_mul_cancel h, mul_one] },
      have c : continuous  (λ x : hat K, x * x₀⁻¹),
        from continuous_id.mul continuous_const,
      rw image_eq_preimage_of_inverse l r,
      rw ← mul_inv_cancel h at V'_in,
      exact c.continuous_at V'_in },
    have : ∃ (z₀ : K) (y₀ ∈ V'), coe z₀ = y₀*x₀ ∧ z₀ ≠ 0,
    { rcases completion.dense_range_coe.mem_nhds nhds_right with ⟨z₀, y₀, y₀_in, H : y₀ * x₀ = z₀⟩,
      refine ⟨z₀, y₀, y₀_in, ⟨H.symm, _⟩⟩,
      rintro rfl,
      exact mul_ne_zero (ne_of_mem_of_not_mem y₀_in zeroV') h H },
    rcases this with ⟨z₀, y₀, y₀_in, hz₀, z₀_ne⟩,
    have vz₀_ne: v z₀ ≠ 0 := by rwa valuation.ne_zero_iff,
    refine ⟨v z₀, _⟩,
    rw [linear_ordered_comm_group_with_zero.tendsto_of_ne_zero vz₀_ne, mem_comap],
    use [(λ x, x*x₀) '' V', nhds_right],
    intros x x_in,
    rcases mem_preimage.1 x_in with ⟨y, y_in, hy⟩, clear x_in,
    change y*x₀ = coe x at hy,
    have : v (x*z₀⁻¹) = 1,
    { apply hV,
      have : ((z₀⁻¹ : K) : hat K) = z₀⁻¹,
      from ring_hom.map_inv (completion.coe_ring_hom : K →+* hat K) z₀,
      rw [completion.coe_mul, this, ← hy, hz₀, mul_inv₀, mul_comm y₀⁻¹, ← mul_assoc, mul_assoc y,
          mul_inv_cancel h, mul_one],
      solve_by_elim },
    calc v x = v (x*z₀⁻¹*z₀) : by rw [mul_assoc, inv_mul_cancel z₀_ne, mul_one]
         ... = v (x*z₀⁻¹)*v z₀ : valuation.map_mul _ _ _
         ... = v z₀ : by rw [this, one_mul]  },
end

@[norm_cast]
lemma valued.extension_extends (x : K) : valued.extension (x : hat K) = v x :=
begin
  haveI : t2_space (valued.Γ₀ K) := regular_space.t2_space _,
  refine completion.dense_inducing_coe.extend_eq_of_tendsto _,
  rw ← completion.dense_inducing_coe.nhds_eq_comap,
  exact valued.continuous_valuation.continuous_at,
end

/-- the extension of a valuation on a division ring to its completion. -/
noncomputable def valued.extension_valuation :
  valuation (hat K) (Γ₀ K) :=
{ to_fun := valued.extension,
  map_zero' := by { simpa [← v.map_zero, ← valued.extension_extends (0 : K)] },
  map_one' := by { rw [← completion.coe_one, valued.extension_extends (1 : K)],
                   exact valuation.map_one _ },
  map_mul' := λ x y, begin
    apply completion.induction_on₂ x y,
    { have c1 : continuous (λ (x : hat K × hat K), valued.extension (x.1 * x.2)),
        from valued.continuous_extension.comp (continuous_fst.mul continuous_snd),

      have c2 : continuous (λ (x : hat K × hat K), valued.extension x.1 * valued.extension x.2),
        from (valued.continuous_extension.comp continuous_fst).mul
              (valued.continuous_extension.comp continuous_snd),
      exact is_closed_eq c1 c2 },
    { intros x y,
      norm_cast,
      exact valuation.map_mul _ _ _ },
  end,
  map_add_le_max' := λ x y, begin
    rw le_max_iff,
    apply completion.induction_on₂ x y,
    { have cont : continuous (valued.extension : hat K → Γ₀ K) := valued.continuous_extension,
      exact  (is_closed_le (cont.comp continuous_add) $ cont.comp continuous_fst).union
        (is_closed_le (cont.comp continuous_add) $ cont.comp continuous_snd) },
    { intros x y,
      dsimp,
      norm_cast,
      rw ← le_max_iff,
      exact v.map_add x y, },
  end }

end valuation_on_valued_field_completion


/-
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
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.finset.fold
import Mathlib.data.equiv.mul_add
import Mathlib.tactic.abel
import Mathlib.PostPort

universes u v w u_1

namespace Mathlib

/-!
# Big operators

In this file we define products and sums indexed by finite sets (specifically, `finset`).

## Notation

We introduce the following notation, localized in `big_operators`.
To enable the notation, use `open_locale big_operators`.

Let `s` be a `finset α`, and `f : α → β` a function.

* `∏ x in s, f x` is notation for `finset.prod s f` (assuming `β` is a `comm_monoid`)
* `∑ x in s, f x` is notation for `finset.sum s f` (assuming `β` is an `add_comm_monoid`)
* `∏ x, f x` is notation for `finset.prod finset.univ f`
  (assuming `α` is a `fintype` and `β` is a `comm_monoid`)
* `∑ x, f x` is notation for `finset.sum finset.univ f`
  (assuming `α` is a `fintype` and `β` is an `add_comm_monoid`)

-/

namespace finset

/--
`∏ x in s, f x` is the product of `f x`
as `x` ranges over the elements of the finite set `s`.
-/
protected def prod {α : Type u} {β : Type v} [comm_monoid β] (s : finset α) (f : α → β) : β :=
  multiset.prod (multiset.map f (val s))

@[simp] theorem prod_mk {α : Type u} {β : Type v} [comm_monoid β] (s : multiset α) (hs : multiset.nodup s) (f : α → β) : finset.prod (mk s hs) f = multiset.prod (multiset.map f s) :=
  rfl

end finset

/--
## Operator precedence of `∏` and `∑`

There is no established mathematical convention
for the operator precedence of big operators like `∏` and `∑`.
We will have to make a choice.

Online discussions, such as https://math.stackexchange.com/q/185538/30839
seem to suggest that `∏` and `∑` should have the same precedence,
and that this should be somewhere between `*` and `+`.
The latter have precedence levels `70` and `65` respectively,
and we therefore choose the level `67`.

In practice, this means that parentheses should be placed as follows:
```lean
∑ k in K, (a k + b k) = ∑ k in K, a k + ∑ k in K, b k →
  ∏ k in K, a k * b k = (∏ k in K, a k) * (∏ k in K, b k)
```
(Example taken from page 490 of Knuth's *Concrete Mathematics*.)
-/
namespace finset

theorem prod_eq_multiset_prod {α : Type u} {β : Type v} [comm_monoid β] (s : finset α) (f : α → β) : (finset.prod s fun (x : α) => f x) = multiset.prod (multiset.map f (val s)) :=
  rfl

theorem prod_eq_fold {α : Type u} {β : Type v} [comm_monoid β] (s : finset α) (f : α → β) : (finset.prod s fun (x : α) => f x) = fold Mul.mul 1 f s :=
  rfl

end finset

theorem monoid_hom.map_prod {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] [comm_monoid γ] (g : β →* γ) (f : α → β) (s : finset α) : coe_fn g (finset.prod s fun (x : α) => f x) = finset.prod s fun (x : α) => coe_fn g (f x) := sorry

theorem add_equiv.map_sum {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] [add_comm_monoid γ] (g : β ≃+ γ) (f : α → β) (s : finset α) : coe_fn g (finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => coe_fn g (f x) :=
  add_monoid_hom.map_sum (add_equiv.to_add_monoid_hom g) f s

theorem ring_hom.map_list_prod {β : Type v} {γ : Type w} [semiring β] [semiring γ] (f : β →+* γ) (l : List β) : coe_fn f (list.prod l) = list.prod (list.map (⇑f) l) :=
  monoid_hom.map_list_prod (ring_hom.to_monoid_hom f) l

theorem ring_hom.map_list_sum {β : Type v} {γ : Type w} [semiring β] [semiring γ] (f : β →+* γ) (l : List β) : coe_fn f (list.sum l) = list.sum (list.map (⇑f) l) :=
  add_monoid_hom.map_list_sum (ring_hom.to_add_monoid_hom f) l

theorem ring_hom.map_multiset_prod {β : Type v} {γ : Type w} [comm_semiring β] [comm_semiring γ] (f : β →+* γ) (s : multiset β) : coe_fn f (multiset.prod s) = multiset.prod (multiset.map (⇑f) s) :=
  monoid_hom.map_multiset_prod (ring_hom.to_monoid_hom f) s

theorem ring_hom.map_multiset_sum {β : Type v} {γ : Type w} [semiring β] [semiring γ] (f : β →+* γ) (s : multiset β) : coe_fn f (multiset.sum s) = multiset.sum (multiset.map (⇑f) s) :=
  add_monoid_hom.map_multiset_sum (ring_hom.to_add_monoid_hom f) s

theorem ring_hom.map_prod {α : Type u} {β : Type v} {γ : Type w} [comm_semiring β] [comm_semiring γ] (g : β →+* γ) (f : α → β) (s : finset α) : coe_fn g (finset.prod s fun (x : α) => f x) = finset.prod s fun (x : α) => coe_fn g (f x) :=
  monoid_hom.map_prod (ring_hom.to_monoid_hom g) f s

theorem ring_hom.map_sum {α : Type u} {β : Type v} {γ : Type w} [semiring β] [semiring γ] (g : β →+* γ) (f : α → β) (s : finset α) : coe_fn g (finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => coe_fn g (f x) :=
  add_monoid_hom.map_sum (ring_hom.to_add_monoid_hom g) f s

theorem add_monoid_hom.coe_sum {α : Type u} {β : Type v} {γ : Type w} [add_monoid β] [add_comm_monoid γ] (f : α → β →+ γ) (s : finset α) : ⇑(finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => ⇑(f x) :=
  add_monoid_hom.map_sum (add_monoid_hom.coe_fn β γ) (fun (x : α) => f x) s

@[simp] theorem monoid_hom.finset_prod_apply {α : Type u} {β : Type v} {γ : Type w} [monoid β] [comm_monoid γ] (f : α → β →* γ) (s : finset α) (b : β) : coe_fn (finset.prod s fun (x : α) => f x) b = finset.prod s fun (x : α) => coe_fn (f x) b :=
  monoid_hom.map_prod (coe_fn monoid_hom.eval b) (fun (x : α) => f x) s

namespace finset

@[simp] theorem sum_empty {β : Type v} [add_comm_monoid β] {α : Type u} {f : α → β} : (finset.sum ∅ fun (x : α) => f x) = 0 :=
  rfl

@[simp] theorem prod_insert {α : Type u} {β : Type v} {s : finset α} {a : α} {f : α → β} [comm_monoid β] [DecidableEq α] : ¬a ∈ s → (finset.prod (insert a s) fun (x : α) => f x) = f a * finset.prod s fun (x : α) => f x :=
  fold_insert

/--
The product of `f` over `insert a s` is the same as
the product over `s`, as long as `a` is in `s` or `f a = 1`.
-/
@[simp] theorem prod_insert_of_eq_one_if_not_mem {α : Type u} {β : Type v} {s : finset α} {a : α} {f : α → β} [comm_monoid β] [DecidableEq α] (h : ¬a ∈ s → f a = 1) : (finset.prod (insert a s) fun (x : α) => f x) = finset.prod s fun (x : α) => f x := sorry

/--
The product of `f` over `insert a s` is the same as the product over `s`, as long as `f a = 1`.
-/
@[simp] theorem prod_insert_one {α : Type u} {β : Type v} {s : finset α} {a : α} {f : α → β} [comm_monoid β] [DecidableEq α] (h : f a = 1) : (finset.prod (insert a s) fun (x : α) => f x) = finset.prod s fun (x : α) => f x :=
  prod_insert_of_eq_one_if_not_mem fun (_x : ¬a ∈ s) => h

@[simp] theorem prod_singleton {α : Type u} {β : Type v} {a : α} {f : α → β} [comm_monoid β] : (finset.prod (singleton a) fun (x : α) => f x) = f a :=
  Eq.trans fold_singleton (mul_one (f a))

theorem sum_pair {α : Type u} {β : Type v} {f : α → β} [add_comm_monoid β] [DecidableEq α] {a : α} {b : α} (h : a ≠ b) : (finset.sum (insert a (singleton b)) fun (x : α) => f x) = f a + f b := sorry

@[simp] theorem prod_const_one {α : Type u} {β : Type v} {s : finset α} [comm_monoid β] : (finset.prod s fun (x : α) => 1) = 1 := sorry

@[simp] theorem sum_const_zero {α : Type u} {β : Type u_1} {s : finset α} [add_comm_monoid β] : (finset.sum s fun (x : α) => 0) = 0 :=
  prod_const_one

@[simp] theorem prod_image {α : Type u} {β : Type v} {γ : Type w} {f : α → β} [comm_monoid β] [DecidableEq α] {s : finset γ} {g : γ → α} : (∀ (x : γ), x ∈ s → ∀ (y : γ), y ∈ s → g x = g y → x = y) →
  (finset.prod (image g s) fun (x : α) => f x) = finset.prod s fun (x : γ) => f (g x) :=
  fold_image

@[simp] theorem prod_map {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] (s : finset α) (e : α ↪ γ) (f : γ → β) : (finset.prod (map e s) fun (x : γ) => f x) = finset.prod s fun (x : α) => f (coe_fn e x) := sorry

theorem prod_congr {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β} {g : α → β} [comm_monoid β] (h : s₁ = s₂) : (∀ (x : α), x ∈ s₂ → f x = g x) → finset.prod s₁ f = finset.prod s₂ g :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((∀ (x : α), x ∈ s₂ → f x = g x) → finset.prod s₁ f = finset.prod s₂ g)) h)) fold_congr

theorem prod_union_inter {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β} [comm_monoid β] [DecidableEq α] : ((finset.prod (s₁ ∪ s₂) fun (x : α) => f x) * finset.prod (s₁ ∩ s₂) fun (x : α) => f x) =
  (finset.prod s₁ fun (x : α) => f x) * finset.prod s₂ fun (x : α) => f x :=
  fold_union_inter

theorem sum_union {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β} [add_comm_monoid β] [DecidableEq α] (h : disjoint s₁ s₂) : (finset.sum (s₁ ∪ s₂) fun (x : α) => f x) = (finset.sum s₁ fun (x : α) => f x) + finset.sum s₂ fun (x : α) => f x := sorry

theorem prod_sdiff {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β} [comm_monoid β] [DecidableEq α] (h : s₁ ⊆ s₂) : ((finset.prod (s₂ \ s₁) fun (x : α) => f x) * finset.prod s₁ fun (x : α) => f x) = finset.prod s₂ fun (x : α) => f x := sorry

@[simp] theorem prod_sum_elim {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] [DecidableEq (α ⊕ γ)] (s : finset α) (t : finset γ) (f : α → β) (g : γ → β) : (finset.prod (map function.embedding.inl s ∪ map function.embedding.inr t) fun (x : α ⊕ γ) => sum.elim f g x) =
  (finset.prod s fun (x : α) => f x) * finset.prod t fun (x : γ) => g x := sorry

theorem sum_bUnion {α : Type u} {β : Type v} {γ : Type w} {f : α → β} [add_comm_monoid β] [DecidableEq α] {s : finset γ} {t : γ → finset α} : (∀ (x : γ), x ∈ s → ∀ (y : γ), y ∈ s → x ≠ y → disjoint (t x) (t y)) →
  (finset.sum (finset.bUnion s t) fun (x : α) => f x) = finset.sum s fun (x : γ) => finset.sum (t x) fun (x : α) => f x := sorry

theorem sum_product {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] {s : finset γ} {t : finset α} {f : γ × α → β} : (finset.sum (finset.product s t) fun (x : γ × α) => f x) =
  finset.sum s fun (x : γ) => finset.sum t fun (y : α) => f (x, y) := sorry

/-- An uncurried version of `finset.prod_product`. -/
theorem sum_product' {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] {s : finset γ} {t : finset α} {f : γ → α → β} : (finset.sum (finset.product s t) fun (x : γ × α) => f (prod.fst x) (prod.snd x)) =
  finset.sum s fun (x : γ) => finset.sum t fun (y : α) => f x y :=
  sum_product

/-- Product over a sigma type equals the product of fiberwise products. For rewriting
in the reverse direction, use `finset.prod_sigma'`.  -/
theorem prod_sigma {α : Type u} {β : Type v} [comm_monoid β] {σ : α → Type u_1} (s : finset α) (t : (a : α) → finset (σ a)) (f : sigma σ → β) : (finset.prod (finset.sigma s t) fun (x : sigma fun (a : α) => σ a) => f x) =
  finset.prod s fun (a : α) => finset.prod (t a) fun (s : σ a) => f (sigma.mk a s) := sorry

theorem prod_sigma' {α : Type u} {β : Type v} [comm_monoid β] {σ : α → Type u_1} (s : finset α) (t : (a : α) → finset (σ a)) (f : (a : α) → σ a → β) : (finset.prod s fun (a : α) => finset.prod (t a) fun (s : σ a) => f a s) =
  finset.prod (finset.sigma s t) fun (x : sigma fun (a : α) => σ a) => f (sigma.fst x) (sigma.snd x) :=
  Eq.symm (prod_sigma s t fun (x : sigma fun (a : α) => σ a) => f (sigma.fst x) (sigma.snd x))

theorem prod_fiberwise_of_maps_to {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] [DecidableEq γ] {s : finset α} {t : finset γ} {g : α → γ} (h : ∀ (x : α), x ∈ s → g x ∈ t) (f : α → β) : (finset.prod t fun (y : γ) => finset.prod (filter (fun (x : α) => g x = y) s) fun (x : α) => f x) =
  finset.prod s fun (x : α) => f x := sorry

theorem prod_image' {α : Type u} {β : Type v} {γ : Type w} {f : α → β} [comm_monoid β] [DecidableEq α] {s : finset γ} {g : γ → α} (h : γ → β) (eq : ∀ (c : γ), c ∈ s → f (g c) = finset.prod (filter (fun (c' : γ) => g c' = g c) s) fun (x : γ) => h x) : (finset.prod (image g s) fun (x : α) => f x) = finset.prod s fun (x : γ) => h x := sorry

theorem prod_mul_distrib {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β} [comm_monoid β] : (finset.prod s fun (x : α) => f x * g x) = (finset.prod s fun (x : α) => f x) * finset.prod s fun (x : α) => g x := sorry

theorem sum_comm {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] {s : finset γ} {t : finset α} {f : γ → α → β} : (finset.sum s fun (x : γ) => finset.sum t fun (y : α) => f x y) =
  finset.sum t fun (y : α) => finset.sum s fun (x : γ) => f x y := sorry

theorem sum_hom {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] [add_comm_monoid γ] (s : finset α) {f : α → β} (g : β → γ) [is_add_monoid_hom g] : (finset.sum s fun (x : α) => g (f x)) = g (finset.sum s fun (x : α) => f x) :=
  Eq.symm (add_monoid_hom.map_sum (add_monoid_hom.of g) f s)

theorem sum_hom_rel {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] [add_comm_monoid γ] {r : β → γ → Prop} {f : α → β} {g : α → γ} {s : finset α} (h₁ : r 0 0) (h₂ : ∀ (a : α) (b : β) (c : γ), r b c → r (f a + b) (g a + c)) : r (finset.sum s fun (x : α) => f x) (finset.sum s fun (x : α) => g x) :=
  id (multiset.sum_hom_rel (val s) h₁ h₂)

theorem prod_subset {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β} [comm_monoid β] (h : s₁ ⊆ s₂) (hf : ∀ (x : α), x ∈ s₂ → ¬x ∈ s₁ → f x = 1) : (finset.prod s₁ fun (x : α) => f x) = finset.prod s₂ fun (x : α) => f x := sorry

theorem prod_filter_of_ne {α : Type u} {β : Type v} {s : finset α} {f : α → β} [comm_monoid β] {p : α → Prop} [decidable_pred p] (hp : ∀ (x : α), x ∈ s → f x ≠ 1 → p x) : (finset.prod (filter p s) fun (x : α) => f x) = finset.prod s fun (x : α) => f x := sorry

-- If we use `[decidable_eq β]` here, some rewrites fail because they find a wrong `decidable`

-- instance first; `{∀x, decidable (f x ≠ 1)}` doesn't work with `rw ← prod_filter_ne_one`

theorem sum_filter_ne_zero {α : Type u} {β : Type v} {s : finset α} {f : α → β} [add_comm_monoid β] [(x : α) → Decidable (f x ≠ 0)] : (finset.sum (filter (fun (x : α) => f x ≠ 0) s) fun (x : α) => f x) = finset.sum s fun (x : α) => f x :=
  sum_filter_of_ne fun (_x : α) (_x_1 : _x ∈ s) => id

theorem sum_filter {α : Type u} {β : Type v} {s : finset α} [add_comm_monoid β] (p : α → Prop) [decidable_pred p] (f : α → β) : (finset.sum (filter p s) fun (a : α) => f a) = finset.sum s fun (a : α) => ite (p a) (f a) 0 := sorry

theorem prod_eq_single {α : Type u} {β : Type v} [comm_monoid β] {s : finset α} {f : α → β} (a : α) (h₀ : ∀ (b : α), b ∈ s → b ≠ a → f b = 1) (h₁ : ¬a ∈ s → f a = 1) : (finset.prod s fun (x : α) => f x) = f a := sorry

theorem sum_attach {α : Type u} {β : Type v} {s : finset α} [add_comm_monoid β] {f : α → β} : (finset.sum (attach s) fun (x : Subtype fun (x : α) => x ∈ s) => f ↑x) = finset.sum s fun (x : α) => f x := sorry

/-- A product over `s.subtype p` equals one over `s.filter p`. -/
@[simp] theorem prod_subtype_eq_prod_filter {α : Type u} {β : Type v} {s : finset α} [comm_monoid β] (f : α → β) {p : α → Prop} [decidable_pred p] : (finset.prod (finset.subtype p s) fun (x : Subtype p) => f ↑x) = finset.prod (filter p s) fun (x : α) => f x := sorry

/-- If all elements of a `finset` satisfy the predicate `p`, a product
over `s.subtype p` equals that product over `s`. -/
theorem sum_subtype_of_mem {α : Type u} {β : Type v} {s : finset α} [add_comm_monoid β] (f : α → β) {p : α → Prop} [decidable_pred p] (h : ∀ (x : α), x ∈ s → p x) : (finset.sum (finset.subtype p s) fun (x : Subtype p) => f ↑x) = finset.sum s fun (x : α) => f x := sorry

/-- A product of a function over a `finset` in a subtype equals a
product in the main type of a function that agrees with the first
function on that `finset`. -/
theorem sum_subtype_map_embedding {α : Type u} {β : Type v} [add_comm_monoid β] {p : α → Prop} {s : finset (Subtype fun (x : α) => p x)} {f : (Subtype fun (x : α) => p x) → β} {g : α → β} (h : ∀ (x : Subtype fun (x : α) => p x), x ∈ s → g ↑x = f x) : (finset.sum (map (function.embedding.subtype fun (x : α) => p x) s) fun (x : α) => g x) =
  finset.sum s fun (x : Subtype fun (x : α) => p x) => f x := sorry

theorem prod_eq_one {α : Type u} {β : Type v} [comm_monoid β] {f : α → β} {s : finset α} (h : ∀ (x : α), x ∈ s → f x = 1) : (finset.prod s fun (x : α) => f x) = 1 :=
  Eq.trans (prod_congr rfl h) prod_const_one

theorem sum_apply_dite {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] {s : finset α} {p : α → Prop} {hp : decidable_pred p} (f : (x : α) → p x → γ) (g : (x : α) → ¬p x → γ) (h : γ → β) : (finset.sum s fun (x : α) => h (dite (p x) (fun (hx : p x) => f x hx) fun (hx : ¬p x) => g x hx)) =
  (finset.sum (attach (filter p s))
      fun (x : Subtype fun (x : α) => x ∈ filter p s) =>
        h (f (subtype.val x) (and.right (iff.mp mem_filter (subtype.property x))))) +
    finset.sum (attach (filter (fun (x : α) => ¬p x) s))
      fun (x : Subtype fun (x : α) => x ∈ filter (fun (x : α) => ¬p x) s) =>
        h (g (subtype.val x) (and.right (iff.mp mem_filter (subtype.property x)))) := sorry

theorem prod_apply_ite {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] {s : finset α} {p : α → Prop} {hp : decidable_pred p} (f : α → γ) (g : α → γ) (h : γ → β) : (finset.prod s fun (x : α) => h (ite (p x) (f x) (g x))) =
  (finset.prod (filter p s) fun (x : α) => h (f x)) *
    finset.prod (filter (fun (x : α) => ¬p x) s) fun (x : α) => h (g x) :=
  trans (prod_apply_dite (fun (x : α) (hx : p x) => f x) (fun (x : α) (hx : ¬p x) => g x) h)
    (congr_arg2 Mul.mul prod_attach prod_attach)

theorem sum_dite {α : Type u} {β : Type v} [add_comm_monoid β] {s : finset α} {p : α → Prop} {hp : decidable_pred p} (f : (x : α) → p x → β) (g : (x : α) → ¬p x → β) : (finset.sum s fun (x : α) => dite (p x) (fun (hx : p x) => f x hx) fun (hx : ¬p x) => g x hx) =
  (finset.sum (attach (filter p s))
      fun (x : Subtype fun (x : α) => x ∈ filter p s) =>
        f (subtype.val x) (and.right (iff.mp mem_filter (subtype.property x)))) +
    finset.sum (attach (filter (fun (x : α) => ¬p x) s))
      fun (x : Subtype fun (x : α) => x ∈ filter (fun (x : α) => ¬p x) s) =>
        g (subtype.val x) (and.right (iff.mp mem_filter (subtype.property x))) := sorry

theorem prod_ite {α : Type u} {β : Type v} [comm_monoid β] {s : finset α} {p : α → Prop} {hp : decidable_pred p} (f : α → β) (g : α → β) : (finset.prod s fun (x : α) => ite (p x) (f x) (g x)) =
  (finset.prod (filter p s) fun (x : α) => f x) * finset.prod (filter (fun (x : α) => ¬p x) s) fun (x : α) => g x := sorry

theorem sum_extend_by_zero {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] (s : finset α) (f : α → β) : (finset.sum s fun (i : α) => ite (i ∈ s) (f i) 0) = finset.sum s fun (i : α) => f i :=
  sum_congr rfl fun (i : α) (hi : i ∈ s) => if_pos hi

@[simp] theorem sum_dite_eq {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] (s : finset α) (a : α) (b : (x : α) → a = x → β) : (finset.sum s fun (x : α) => dite (a = x) (fun (h : a = x) => b x h) fun (h : ¬a = x) => 0) = ite (a ∈ s) (b a rfl) 0 := sorry

@[simp] theorem prod_dite_eq' {α : Type u} {β : Type v} [comm_monoid β] [DecidableEq α] (s : finset α) (a : α) (b : (x : α) → x = a → β) : (finset.prod s fun (x : α) => dite (x = a) (fun (h : x = a) => b x h) fun (h : ¬x = a) => 1) = ite (a ∈ s) (b a rfl) 1 := sorry

@[simp] theorem prod_ite_eq {α : Type u} {β : Type v} [comm_monoid β] [DecidableEq α] (s : finset α) (a : α) (b : α → β) : (finset.prod s fun (x : α) => ite (a = x) (b x) 1) = ite (a ∈ s) (b a) 1 :=
  prod_dite_eq s a fun (x : α) (_x : a = x) => b x

/--
  When a product is taken over a conditional whose condition is an equality test on the index
  and whose alternative is 1, then the product's value is either the term at that index or `1`.

  The difference with `prod_ite_eq` is that the arguments to `eq` are swapped.
-/
@[simp] theorem sum_ite_eq' {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] (s : finset α) (a : α) (b : α → β) : (finset.sum s fun (x : α) => ite (x = a) (b x) 0) = ite (a ∈ s) (b a) 0 :=
  sum_dite_eq' s a fun (x : α) (_x : x = a) => b x

theorem prod_ite_index {α : Type u} {β : Type v} [comm_monoid β] (p : Prop) [Decidable p] (s : finset α) (t : finset α) (f : α → β) : (finset.prod (ite p s t) fun (x : α) => f x) =
  ite p (finset.prod s fun (x : α) => f x) (finset.prod t fun (x : α) => f x) :=
  apply_ite (fun (s : finset α) => finset.prod s fun (x : α) => f x) p s t

/--
  Reorder a product.

  The difference with `prod_bij'` is that the bijection is specified as a surjective injection,
  rather than by an inverse function.
-/
theorem prod_bij {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] {s : finset α} {t : finset γ} {f : α → β} {g : γ → β} (i : (a : α) → a ∈ s → γ) (hi : ∀ (a : α) (ha : a ∈ s), i a ha ∈ t) (h : ∀ (a : α) (ha : a ∈ s), f a = g (i a ha)) (i_inj : ∀ (a₁ a₂ : α) (ha₁ : a₁ ∈ s) (ha₂ : a₂ ∈ s), i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂) (i_surj : ∀ (b : γ), b ∈ t → ∃ (a : α), ∃ (ha : a ∈ s), b = i a ha) : (finset.prod s fun (x : α) => f x) = finset.prod t fun (x : γ) => g x :=
  congr_arg multiset.prod (multiset.map_eq_map_of_bij_of_nodup f g (nodup s) (nodup t) i hi h i_inj i_surj)

/--
  Reorder a product.

  The difference with `prod_bij` is that the bijection is specified with an inverse, rather than
  as a surjective injection.
-/
theorem sum_bij' {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] {s : finset α} {t : finset γ} {f : α → β} {g : γ → β} (i : (a : α) → a ∈ s → γ) (hi : ∀ (a : α) (ha : a ∈ s), i a ha ∈ t) (h : ∀ (a : α) (ha : a ∈ s), f a = g (i a ha)) (j : (a : γ) → a ∈ t → α) (hj : ∀ (a : γ) (ha : a ∈ t), j a ha ∈ s) (left_inv : ∀ (a : α) (ha : a ∈ s), j (i a ha) (hi a ha) = a) (right_inv : ∀ (a : γ) (ha : a ∈ t), i (j a ha) (hj a ha) = a) : (finset.sum s fun (x : α) => f x) = finset.sum t fun (x : γ) => g x := sorry

theorem prod_bij_ne_one {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] {s : finset α} {t : finset γ} {f : α → β} {g : γ → β} (i : (a : α) → a ∈ s → f a ≠ 1 → γ) (hi : ∀ (a : α) (h₁ : a ∈ s) (h₂ : f a ≠ 1), i a h₁ h₂ ∈ t) (i_inj : ∀ (a₁ a₂ : α) (h₁₁ : a₁ ∈ s) (h₁₂ : f a₁ ≠ 1) (h₂₁ : a₂ ∈ s) (h₂₂ : f a₂ ≠ 1), i a₁ h₁₁ h₁₂ = i a₂ h₂₁ h₂₂ → a₁ = a₂) (i_surj : ∀ (b : γ), b ∈ t → g b ≠ 1 → ∃ (a : α), ∃ (h₁ : a ∈ s), ∃ (h₂ : f a ≠ 1), b = i a h₁ h₂) (h : ∀ (a : α) (h₁ : a ∈ s) (h₂ : f a ≠ 1), f a = g (i a h₁ h₂)) : (finset.prod s fun (x : α) => f x) = finset.prod t fun (x : γ) => g x := sorry

theorem nonempty_of_sum_ne_zero {α : Type u} {β : Type v} {s : finset α} {f : α → β} [add_comm_monoid β] (h : (finset.sum s fun (x : α) => f x) ≠ 0) : finset.nonempty s :=
  or.elim (eq_empty_or_nonempty s) (fun (H : s = ∅) => false.elim (h (Eq.symm H ▸ sum_empty))) id

theorem exists_ne_zero_of_sum_ne_zero {α : Type u} {β : Type v} {s : finset α} {f : α → β} [add_comm_monoid β] (h : (finset.sum s fun (x : α) => f x) ≠ 0) : ∃ (a : α), ∃ (H : a ∈ s), f a ≠ 0 := sorry

theorem sum_subset_zero_on_sdiff {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β} {g : α → β} [add_comm_monoid β] [DecidableEq α] (h : s₁ ⊆ s₂) (hg : ∀ (x : α), x ∈ s₂ \ s₁ → g x = 0) (hfg : ∀ (x : α), x ∈ s₁ → f x = g x) : (finset.sum s₁ fun (i : α) => f i) = finset.sum s₂ fun (i : α) => g i := sorry

theorem sum_range_succ {β : Type u_1} [add_comm_monoid β] (f : ℕ → β) (n : ℕ) : (finset.sum (range (n + 1)) fun (x : ℕ) => f x) = f n + finset.sum (range n) fun (x : ℕ) => f x := sorry

theorem prod_range_succ {β : Type v} [comm_monoid β] (f : ℕ → β) (n : ℕ) : (finset.prod (range (n + 1)) fun (x : ℕ) => f x) = f n * finset.prod (range n) fun (x : ℕ) => f x := sorry

theorem prod_range_succ' {β : Type v} [comm_monoid β] (f : ℕ → β) (n : ℕ) : (finset.prod (range (n + 1)) fun (k : ℕ) => f k) = (finset.prod (range n) fun (k : ℕ) => f (k + 1)) * f 0 := sorry

theorem prod_range_zero {β : Type v} [comm_monoid β] (f : ℕ → β) : (finset.prod (range 0) fun (k : ℕ) => f k) = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((finset.prod (range 0) fun (k : ℕ) => f k) = 1)) range_zero))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((finset.prod ∅ fun (k : ℕ) => f k) = 1)) prod_empty)) (Eq.refl 1))

theorem prod_range_one {β : Type v} [comm_monoid β] (f : ℕ → β) : (finset.prod (range 1) fun (k : ℕ) => f k) = f 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((finset.prod (range 1) fun (k : ℕ) => f k) = f 0)) range_one)) prod_singleton

theorem sum_range_one {δ : Type u_1} [add_comm_monoid δ] (f : ℕ → δ) : (finset.sum (range 1) fun (k : ℕ) => f k) = f 0 :=
  prod_range_one f

theorem prod_multiset_map_count {α : Type u} [DecidableEq α] (s : multiset α) {M : Type u_1} [comm_monoid M] (f : α → M) : multiset.prod (multiset.map f s) = finset.prod (multiset.to_finset s) fun (m : α) => f m ^ multiset.count m s := sorry

theorem prod_multiset_count {α : Type u} [DecidableEq α] [comm_monoid α] (s : multiset α) : multiset.prod s = finset.prod (multiset.to_finset s) fun (m : α) => m ^ multiset.count m s := sorry

/--
To prove a property of a product, it suffices to prove that
the property is multiplicative and holds on factors.
-/
theorem prod_induction {α : Type u} {s : finset α} {M : Type u_1} [comm_monoid M] (f : α → M) (p : M → Prop) (p_mul : ∀ (a b : M), p a → p b → p (a * b)) (p_one : p 1) (p_s : ∀ (x : α), x ∈ s → p (f x)) : p (finset.prod s fun (x : α) => f x) := sorry

/--
For any product along `{0, ..., n-1}` of a commutative-monoid-valued function, we can verify that
it's equal to a different function just by checking ratios of adjacent terms.
This is a multiplicative discrete analogue of the fundamental theorem of calculus. -/
theorem prod_range_induction {M : Type u_1} [comm_monoid M] (f : ℕ → M) (s : ℕ → M) (h0 : s 0 = 1) (h : ∀ (n : ℕ), s (n + 1) = s n * f n) (n : ℕ) : (finset.prod (range n) fun (k : ℕ) => f k) = s n := sorry

/--
For any sum along `{0, ..., n-1}` of a commutative-monoid-valued function,
we can verify that it's equal to a different function
just by checking differences of adjacent terms.
This is a discrete analogue
of the fundamental theorem of calculus.
-/
theorem sum_range_induction {M : Type u_1} [add_comm_monoid M] (f : ℕ → M) (s : ℕ → M) (h0 : s 0 = 0) (h : ∀ (n : ℕ), s (n + 1) = s n + f n) (n : ℕ) : (finset.sum (range n) fun (k : ℕ) => f k) = s n :=
  prod_range_induction f s h0 h n

/-- A telescoping sum along `{0, ..., n-1}` of an additive commutative group valued function
reduces to the difference of the last and first terms.-/
theorem sum_range_sub {G : Type u_1} [add_comm_group G] (f : ℕ → G) (n : ℕ) : (finset.sum (range n) fun (i : ℕ) => f (i + 1) - f i) = f n - f 0 := sorry

theorem sum_range_sub' {G : Type u_1} [add_comm_group G] (f : ℕ → G) (n : ℕ) : (finset.sum (range n) fun (i : ℕ) => f i - f (i + 1)) = f 0 - f n := sorry

/-- A telescoping product along `{0, ..., n-1}` of a commutative group valued function
reduces to the ratio of the last and first factors.-/
theorem prod_range_div {M : Type u_1} [comm_group M] (f : ℕ → M) (n : ℕ) : (finset.prod (range n) fun (i : ℕ) => f (i + 1) * (f i⁻¹)) = f n * (f 0⁻¹) := sorry

theorem prod_range_div' {M : Type u_1} [comm_group M] (f : ℕ → M) (n : ℕ) : (finset.prod (range n) fun (i : ℕ) => f i * (f (i + 1)⁻¹)) = f 0 * (f n⁻¹) := sorry

/--
A telescoping sum along `{0, ..., n-1}` of an `ℕ`-valued function
reduces to the difference of the last and first terms
when the function we are summing is monotone.
-/
theorem sum_range_sub_of_monotone {f : ℕ → ℕ} (h : monotone f) (n : ℕ) : (finset.sum (range n) fun (i : ℕ) => f (i + 1) - f i) = f n - f 0 := sorry

@[simp] theorem prod_const {α : Type u} {β : Type v} {s : finset α} [comm_monoid β] (b : β) : (finset.prod s fun (x : α) => b) = b ^ card s := sorry

theorem pow_eq_prod_const {β : Type v} [comm_monoid β] (b : β) (n : ℕ) : b ^ n = finset.prod (range n) fun (k : ℕ) => b := sorry

theorem prod_pow {α : Type u} {β : Type v} [comm_monoid β] (s : finset α) (n : ℕ) (f : α → β) : (finset.prod s fun (x : α) => f x ^ n) = (finset.prod s fun (x : α) => f x) ^ n := sorry

-- `to_additive` fails on this lemma, so we prove it manually below

theorem prod_flip {β : Type v} [comm_monoid β] {n : ℕ} (f : ℕ → β) : (finset.prod (range (n + 1)) fun (r : ℕ) => f (n - r)) = finset.prod (range (n + 1)) fun (k : ℕ) => f k := sorry

theorem sum_involution {α : Type u} {β : Type v} [add_comm_monoid β] {s : finset α} {f : α → β} (g : (a : α) → a ∈ s → α) (h : ∀ (a : α) (ha : a ∈ s), f a + f (g a ha) = 0) (g_ne : ∀ (a : α) (ha : a ∈ s), f a ≠ 0 → g a ha ≠ a) (g_mem : ∀ (a : α) (ha : a ∈ s), g a ha ∈ s) (g_inv : ∀ (a : α) (ha : a ∈ s), g (g a ha) (g_mem a ha) = a) : (finset.sum s fun (x : α) => f x) = 0 := sorry

/-- The product of the composition of functions `f` and `g`, is the product
over `b ∈ s.image g` of `f b` to the power of the cardinality of the fibre of `b` -/
theorem prod_comp {α : Type u} {β : Type v} {γ : Type w} [comm_monoid β] [DecidableEq γ] {s : finset α} (f : γ → β) (g : α → γ) : (finset.prod s fun (a : α) => f (g a)) =
  finset.prod (image g s) fun (b : γ) => f b ^ card (filter (fun (a : α) => g a = b) s) := sorry

theorem sum_piecewise {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] (s : finset α) (t : finset α) (f : α → β) (g : α → β) : (finset.sum s fun (x : α) => piecewise t f g x) =
  (finset.sum (s ∩ t) fun (x : α) => f x) + finset.sum (s \ t) fun (x : α) => g x := sorry

theorem sum_inter_add_sum_diff {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] (s : finset α) (t : finset α) (f : α → β) : ((finset.sum (s ∩ t) fun (x : α) => f x) + finset.sum (s \ t) fun (x : α) => f x) = finset.sum s fun (x : α) => f x := sorry

theorem mul_prod_diff_singleton {α : Type u} {β : Type v} [comm_monoid β] [DecidableEq α] {s : finset α} {i : α} (h : i ∈ s) (f : α → β) : (f i * finset.prod (s \ singleton i) fun (x : α) => f x) = finset.prod s fun (x : α) => f x := sorry

/-- A product can be partitioned into a product of products, each equivalent under a setoid. -/
theorem sum_partition {α : Type u} {β : Type v} {s : finset α} {f : α → β} [add_comm_monoid β] (R : setoid α) [DecidableRel setoid.r] : (finset.sum s fun (x : α) => f x) =
  finset.sum (image quotient.mk s)
    fun (xbar : quotient R) => finset.sum (filter (fun (y : α) => quotient.mk y = xbar) s) fun (x : α) => f x := sorry

/-- If we can partition a product into subsets that cancel out, then the whole product cancels. -/
theorem prod_cancels_of_partition_cancels {α : Type u} {β : Type v} {s : finset α} {f : α → β} [comm_monoid β] (R : setoid α) [DecidableRel setoid.r] (h : ∀ (x : α), x ∈ s → (finset.prod (filter (fun (y : α) => y ≈ x) s) fun (a : α) => f a) = 1) : (finset.prod s fun (a : α) => f a) = 1 := sorry

theorem sum_update_of_not_mem {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] {s : finset α} {i : α} (h : ¬i ∈ s) (f : α → β) (b : β) : (finset.sum s fun (x : α) => function.update f i b x) = finset.sum s fun (x : α) => f x := sorry

theorem prod_update_of_mem {α : Type u} {β : Type v} [comm_monoid β] [DecidableEq α] {s : finset α} {i : α} (h : i ∈ s) (f : α → β) (b : β) : (finset.prod s fun (x : α) => function.update f i b x) = b * finset.prod (s \ singleton i) fun (x : α) => f x := sorry

/-- If a product of a `finset` of size at most 1 has a given value, so
do the terms in that product. -/
theorem eq_of_card_le_one_of_prod_eq {α : Type u} {β : Type v} [comm_monoid β] {s : finset α} (hc : card s ≤ 1) {f : α → β} {b : β} (h : (finset.prod s fun (x : α) => f x) = b) (x : α) (H : x ∈ s) : f x = b := sorry

/-- If a sum of a `finset` of size at most 1 has a given value, so do
the terms in that sum. -/
theorem eq_of_card_le_one_of_sum_eq {α : Type u} {γ : Type w} [add_comm_monoid γ] {s : finset α} (hc : card s ≤ 1) {f : α → γ} {b : γ} (h : (finset.sum s fun (x : α) => f x) = b) (x : α) (H : x ∈ s) : f x = b := sorry

/-- If a function applied at a point is 1, a product is unchanged by
removing that point, if present, from a `finset`. -/
theorem prod_erase {α : Type u} {β : Type v} [comm_monoid β] [DecidableEq α] (s : finset α) {f : α → β} {a : α} (h : f a = 1) : (finset.prod (erase s a) fun (x : α) => f x) = finset.prod s fun (x : α) => f x := sorry

/-- If a product is 1 and the function is 1 except possibly at one
point, it is 1 everywhere on the `finset`. -/
theorem eq_one_of_prod_eq_one {α : Type u} {β : Type v} [comm_monoid β] {s : finset α} {f : α → β} {a : α} (hp : (finset.prod s fun (x : α) => f x) = 1) (h1 : ∀ (x : α), x ∈ s → x ≠ a → f x = 1) (x : α) (H : x ∈ s) : f x = 1 := sorry

theorem prod_pow_boole {α : Type u} {β : Type v} [comm_monoid β] [DecidableEq α] (s : finset α) (f : α → β) (a : α) : (finset.prod s fun (x : α) => f x ^ ite (a = x) 1 0) = ite (a ∈ s) (f a) 1 := sorry

/-- If `f = g = h` everywhere but at `i`, where `f i = g i + h i`, then the product of `f` over `s`
  is the sum of the products of `g` and `h`. -/
theorem prod_add_prod_eq {α : Type u} {β : Type v} [comm_semiring β] {s : finset α} {i : α} {f : α → β} {g : α → β} {h : α → β} (hi : i ∈ s) (h1 : g i + h i = f i) (h2 : ∀ (j : α), j ∈ s → j ≠ i → g j = f j) (h3 : ∀ (j : α), j ∈ s → j ≠ i → h j = f j) : ((finset.prod s fun (i : α) => g i) + finset.prod s fun (i : α) => h i) = finset.prod s fun (i : α) => f i := sorry

theorem sum_update_of_mem {α : Type u} {β : Type v} [add_comm_monoid β] [DecidableEq α] {s : finset α} {i : α} (h : i ∈ s) (f : α → β) (b : β) : (finset.sum s fun (x : α) => function.update f i b x) = b + finset.sum (s \ singleton i) fun (x : α) => f x := sorry

theorem sum_nsmul {α : Type u} {β : Type v} [add_comm_monoid β] (s : finset α) (n : ℕ) (f : α → β) : (finset.sum s fun (x : α) => n •ℕ f x) = n •ℕ finset.sum s fun (x : α) => f x :=
  prod_pow s n fun (x : α) => f x

@[simp] theorem sum_const {α : Type u} {β : Type v} {s : finset α} [add_comm_monoid β] (b : β) : (finset.sum s fun (x : α) => b) = card s •ℕ b :=
  prod_const b

theorem card_eq_sum_ones {α : Type u} (s : finset α) : card s = finset.sum s fun (_x : α) => 1 := sorry

theorem sum_const_nat {α : Type u} {s : finset α} {m : ℕ} {f : α → ℕ} (h₁ : ∀ (x : α), x ∈ s → f x = m) : (finset.sum s fun (x : α) => f x) = card s * m := sorry

@[simp] theorem sum_boole {α : Type u} {β : Type v} {s : finset α} {p : α → Prop} [semiring β] {hp : decidable_pred p} : (finset.sum s fun (x : α) => ite (p x) 1 0) = ↑(card (filter p s)) := sorry

theorem sum_nat_cast {α : Type u} {β : Type v} [add_comm_monoid β] [HasOne β] (s : finset α) (f : α → ℕ) : ↑(finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => ↑(f x) :=
  add_monoid_hom.map_sum (nat.cast_add_monoid_hom β) f s

theorem sum_int_cast {α : Type u} {β : Type v} [add_comm_group β] [HasOne β] (s : finset α) (f : α → ℤ) : ↑(finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => ↑(f x) :=
  add_monoid_hom.map_sum (int.cast_add_hom β) f s

theorem sum_comp {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid β] [DecidableEq γ] {s : finset α} (f : γ → β) (g : α → γ) : (finset.sum s fun (a : α) => f (g a)) =
  finset.sum (image g s) fun (b : γ) => card (filter (fun (a : α) => g a = b) s) •ℕ f b :=
  prod_comp f fun (a : α) => g a

theorem sum_range_succ' {β : Type v} [add_comm_monoid β] (f : ℕ → β) (n : ℕ) : (finset.sum (range (n + 1)) fun (i : ℕ) => f i) = (finset.sum (range n) fun (i : ℕ) => f (i + 1)) + f 0 :=
  prod_range_succ' fun (k : ℕ) => f k

theorem sum_flip {β : Type v} [add_comm_monoid β] {n : ℕ} (f : ℕ → β) : (finset.sum (range (n + 1)) fun (i : ℕ) => f (n - i)) = finset.sum (range (n + 1)) fun (i : ℕ) => f i :=
  prod_flip f

/-- Moving to the opposite additive commutative monoid commutes with summing. -/
@[simp] theorem op_sum {α : Type u} {β : Type v} [add_comm_monoid β] {s : finset α} (f : α → β) : opposite.op (finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => opposite.op (f x) :=
  add_equiv.map_sum opposite.op_add_equiv (fun (x : α) => f x) s

@[simp] theorem unop_sum {α : Type u} {β : Type v} [add_comm_monoid β] {s : finset α} (f : α → (βᵒᵖ)) : opposite.unop (finset.sum s fun (x : α) => f x) = finset.sum s fun (x : α) => opposite.unop (f x) :=
  add_equiv.map_sum (add_equiv.symm opposite.op_add_equiv) (fun (x : α) => f x) s

@[simp] theorem sum_neg_distrib {α : Type u} {β : Type v} {s : finset α} {f : α → β} [add_comm_group β] : (finset.sum s fun (x : α) => -f x) = -finset.sum s fun (x : α) => f x :=
  sum_hom s Neg.neg

@[simp] theorem card_sigma {α : Type u} {σ : α → Type u_1} (s : finset α) (t : (a : α) → finset (σ a)) : card (finset.sigma s t) = finset.sum s fun (a : α) => card (t a) :=
  multiset.card_sigma (val s) fun (a : α) => (fun (a : α) => val (t a)) a

theorem card_bUnion {α : Type u} {β : Type v} [DecidableEq β] {s : finset α} {t : α → finset β} (h : ∀ (x : α), x ∈ s → ∀ (y : α), y ∈ s → x ≠ y → disjoint (t x) (t y)) : card (finset.bUnion s t) = finset.sum s fun (u : α) => card (t u) := sorry

theorem card_bUnion_le {α : Type u} {β : Type v} [DecidableEq β] {s : finset α} {t : α → finset β} : card (finset.bUnion s t) ≤ finset.sum s fun (a : α) => card (t a) := sorry

theorem card_eq_sum_card_fiberwise {α : Type u} {β : Type v} [DecidableEq β] {f : α → β} {s : finset α} {t : finset β} (H : ∀ (x : α), x ∈ s → f x ∈ t) : card s = finset.sum t fun (a : β) => card (filter (fun (x : α) => f x = a) s) := sorry

theorem card_eq_sum_card_image {α : Type u} {β : Type v} [DecidableEq β] (f : α → β) (s : finset α) : card s = finset.sum (image f s) fun (a : β) => card (filter (fun (x : α) => f x = a) s) :=
  card_eq_sum_card_fiberwise fun (_x : α) => mem_image_of_mem fun (x : α) => f x

theorem gsmul_sum {α : Type u} {β : Type v} [add_comm_group β] {f : α → β} {s : finset α} (z : ℤ) : (z •ℤ finset.sum s fun (a : α) => f a) = finset.sum s fun (a : α) => z •ℤ f a :=
  Eq.symm (sum_hom s (gsmul z))

@[simp] theorem sum_sub_distrib {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β} [add_comm_group β] : (finset.sum s fun (x : α) => f x - g x) = (finset.sum s fun (x : α) => f x) - finset.sum s fun (x : α) => g x := sorry

theorem prod_eq_zero {α : Type u} {β : Type v} {s : finset α} {a : α} {f : α → β} [comm_monoid_with_zero β] (ha : a ∈ s) (h : f a = 0) : (finset.prod s fun (x : α) => f x) = 0 := sorry

theorem prod_boole {α : Type u} {β : Type v} [comm_monoid_with_zero β] {s : finset α} {p : α → Prop} [decidable_pred p] : (finset.prod s fun (i : α) => ite (p i) 1 0) = ite (∀ (i : α), i ∈ s → p i) 1 0 := sorry

theorem prod_eq_zero_iff {α : Type u} {β : Type v} {s : finset α} {f : α → β} [comm_monoid_with_zero β] [nontrivial β] [no_zero_divisors β] : (finset.prod s fun (x : α) => f x) = 0 ↔ ∃ (a : α), ∃ (H : a ∈ s), f a = 0 := sorry

theorem prod_ne_zero_iff {α : Type u} {β : Type v} {s : finset α} {f : α → β} [comm_monoid_with_zero β] [nontrivial β] [no_zero_divisors β] : (finset.prod s fun (x : α) => f x) ≠ 0 ↔ ∀ (a : α), a ∈ s → f a ≠ 0 := sorry

@[simp] theorem prod_inv_distrib' {α : Type u} {β : Type v} {s : finset α} {f : α → β} [comm_group_with_zero β] : (finset.prod s fun (x : α) => f x⁻¹) = ((finset.prod s fun (x : α) => f x)⁻¹) := sorry

end finset

namespace list

theorem prod_to_finset {α : Type u} {M : Type u_1} [DecidableEq α] [comm_monoid M] (f : α → M) {l : List α} (hl : nodup l) : finset.prod (to_finset l) f = prod (map f l) := sorry

end list

namespace multiset

@[simp] theorem to_finset_sum_count_eq {α : Type u} [DecidableEq α] (s : multiset α) : (finset.sum (to_finset s) fun (a : α) => count a s) = coe_fn card s := sorry

theorem count_sum' {α : Type u} {β : Type v} [DecidableEq α] {s : finset β} {a : α} {f : β → multiset α} : count a (finset.sum s fun (x : β) => f x) = finset.sum s fun (x : β) => count a (f x) := sorry

@[simp] theorem to_finset_sum_count_smul_eq {α : Type u} [DecidableEq α] (s : multiset α) : (finset.sum (to_finset s) fun (a : α) => count a s •ℕ (a ::ₘ 0)) = s := sorry

theorem exists_smul_of_dvd_count {α : Type u} [DecidableEq α] (s : multiset α) {k : ℕ} (h : ∀ (a : α), k ∣ count a s) : ∃ (u : multiset α), s = k •ℕ u := sorry

end multiset

@[simp] theorem nat.coe_prod {α : Type u} {R : Type u_1} [comm_semiring R] (f : α → ℕ) (s : finset α) : ↑(finset.prod s fun (i : α) => f i) = finset.prod s fun (i : α) => ↑(f i) :=
  ring_hom.map_prod (nat.cast_ring_hom R) (fun (i : α) => f i) s

@[simp] theorem int.coe_prod {α : Type u} {R : Type u_1} [comm_ring R] (f : α → ℤ) (s : finset α) : ↑(finset.prod s fun (i : α) => f i) = finset.prod s fun (i : α) => ↑(f i) :=
  ring_hom.map_prod (int.cast_ring_hom R) (fun (i : α) => f i) s

@[simp] theorem units.coe_prod {α : Type u} {M : Type u_1} [comm_monoid M] (f : α → units M) (s : finset α) : ↑(finset.prod s fun (i : α) => f i) = finset.prod s fun (i : α) => ↑(f i) :=
  monoid_hom.map_prod (units.coe_hom M) (fun (i : α) => f i) s