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

! This file was ported from Lean 3 source module probability.martingale.borel_cantelli
! leanprover-community/mathlib commit 2196ab363eb097c008d4497125e0dde23fb36db2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Probability.Martingale.Convergence
import Mathbin.Probability.Martingale.OptionalStopping
import Mathbin.Probability.Martingale.Centering

/-!

# Generalized Borel-Cantelli lemma

This file proves Lévy's generalized Borel-Cantelli lemma which is a generalization of the
Borel-Cantelli lemmas. With this generalization, one can easily deduce the Borel-Cantelli lemmas
by choosing appropriate filtrations. This file also contains the one sided martingale bound which
is required to prove the generalized Borel-Cantelli.

## Main results

- `measure_theory.submartingale.bdd_above_iff_exists_tendsto`: the one sided martingale bound: given
  a submartingale `f` with uniformly bounded differences, the set for which `f` converges is almost
  everywhere equal to the set for which it is bounded.
- `measure_theory.ae_mem_limsup_at_top_iff`: Lévy's generalized Borel-Cantelli:
  given a filtration `ℱ` and a sequence of sets `s` such that `s n ∈ ℱ n` for all `n`,
  `limsup at_top s` is almost everywhere equal to the set for which `∑ ℙ[s (n + 1)∣ℱ n] = ∞`.

-/

open Filter

open NNReal ENNReal MeasureTheory ProbabilityTheory BigOperators Topology

namespace MeasureTheory

variable {Ω : Type _} {m0 : MeasurableSpace Ω} {μ : Measure Ω} {ℱ : Filtration ℕ m0} {f : ℕ → Ω → ℝ}
  {ω : Ω}

/-!
### One sided martingale bound
-/

-- TODO: `least_ge` should be defined taking values in `with_top ℕ` once the `stopped_process`
-- refactor is complete
/-- `least_ge f r n` is the stopping time corresponding to the first time `f ≥ r`. -/
noncomputable def leastGe (f : ℕ → Ω → ℝ) (r : ℝ) (n : ℕ) :=
  hitting f (Set.Ici r) 0 n
#align measure_theory.least_ge MeasureTheory.leastGe

theorem Adapted.isStoppingTime_leastGe (r : ℝ) (n : ℕ) (hf : Adapted ℱ f) :
    IsStoppingTime ℱ (leastGe f r n) :=
  hitting_isStoppingTime hf measurableSet_Ici
#align measure_theory.adapted.is_stopping_time_least_ge MeasureTheory.Adapted.isStoppingTime_leastGe

theorem leastGe_le {i : ℕ} {r : ℝ} (ω : Ω) : leastGe f r i ω ≤ i :=
  hitting_le ω
#align measure_theory.least_ge_le MeasureTheory.leastGe_le

-- The following four lemmas shows `least_ge` behaves like a stopped process. Ideally we should
-- define `least_ge` as a stopping time and take its stopped process. However, we can't do that
-- with our current definition since a stopping time takes only finite indicies. An upcomming
-- refactor should hopefully make it possible to have stopping times taking infinity as a value
theorem leastGe_mono {n m : ℕ} (hnm : n ≤ m) (r : ℝ) (ω : Ω) : leastGe f r n ω ≤ leastGe f r m ω :=
  hitting_mono hnm
#align measure_theory.least_ge_mono MeasureTheory.leastGe_mono

theorem leastGe_eq_min (π : Ω → ℕ) (r : ℝ) (ω : Ω) {n : ℕ} (hπn : ∀ ω, π ω ≤ n) :
    leastGe f r (π ω) ω = min (π ω) (leastGe f r n ω) := by
  classical
    refine' le_antisymm (le_min (least_ge_le _) (least_ge_mono (hπn ω) r ω)) _
    by_cases hle : π ω ≤ least_ge f r n ω
    · rw [min_eq_left hle, least_ge]
      by_cases h : ∃ j ∈ Set.Icc 0 (π ω), f j ω ∈ Set.Ici r
      · refine' hle.trans (Eq.le _)
        rw [least_ge, ← hitting_eq_hitting_of_exists (hπn ω) h]
      · simp only [hitting, if_neg h]
    · rw [min_eq_right (not_le.1 hle).le, least_ge, least_ge, ←
        hitting_eq_hitting_of_exists (hπn ω) _]
      rw [not_le, least_ge, hitting_lt_iff _ (hπn ω)] at hle
      exact
        let ⟨j, hj₁, hj₂⟩ := hle
        ⟨j, ⟨hj₁.1, hj₁.2.le⟩, hj₂⟩
#align measure_theory.least_ge_eq_min MeasureTheory.leastGe_eq_min

theorem stoppedValue_stoppedValue_leastGe (f : ℕ → Ω → ℝ) (π : Ω → ℕ) (r : ℝ) {n : ℕ}
    (hπn : ∀ ω, π ω ≤ n) :
    stoppedValue (fun i => stoppedValue f (leastGe f r i)) π =
      stoppedValue (stoppedProcess f (leastGe f r n)) π :=
  by
  ext1 ω
  simp_rw [stopped_process, stopped_value]
  rw [least_ge_eq_min _ _ _ hπn]
#align measure_theory.stopped_value_stopped_value_least_ge MeasureTheory.stoppedValue_stoppedValue_leastGe

theorem Submartingale.stoppedValueLeastGe [IsFiniteMeasure μ] (hf : Submartingale f ℱ μ) (r : ℝ) :
    Submartingale (fun i => stoppedValue f (leastGe f r i)) ℱ μ :=
  by
  rw [submartingale_iff_expected_stopped_value_mono]
  · intro σ π hσ hπ hσ_le_π hπ_bdd
    obtain ⟨n, hπ_le_n⟩ := hπ_bdd
    simp_rw [stopped_value_stopped_value_least_ge f σ r fun i => (hσ_le_π i).trans (hπ_le_n i)]
    simp_rw [stopped_value_stopped_value_least_ge f π r hπ_le_n]
    refine' hf.expected_stopped_value_mono _ _ _ fun ω => (min_le_left _ _).trans (hπ_le_n ω)
    · exact hσ.min (hf.adapted.is_stopping_time_least_ge _ _)
    · exact hπ.min (hf.adapted.is_stopping_time_least_ge _ _)
    · exact fun ω => min_le_min (hσ_le_π ω) le_rfl
  ·
    exact fun i =>
      strongly_measurable_stopped_value_of_le hf.adapted.prog_measurable_of_discrete
        (hf.adapted.is_stopping_time_least_ge _ _) least_ge_le
  ·
    exact fun i =>
      integrable_stopped_value _ (hf.adapted.is_stopping_time_least_ge _ _) hf.integrable
        least_ge_le
#align measure_theory.submartingale.stopped_value_least_ge MeasureTheory.Submartingale.stoppedValueLeastGe

variable {r : ℝ} {R : ℝ≥0}

theorem norm_stoppedValue_leastGe_le (hr : 0 ≤ r) (hf0 : f 0 = 0)
    (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) (i : ℕ) :
    ∀ᵐ ω ∂μ, stoppedValue f (leastGe f r i) ω ≤ r + R :=
  by
  filter_upwards [hbdd]with ω hbddω
  change f (least_ge f r i ω) ω ≤ r + R
  by_cases heq : least_ge f r i ω = 0
  · rw [HEq, hf0, Pi.zero_apply]
    exact add_nonneg hr R.coe_nonneg
  · obtain ⟨k, hk⟩ := Nat.exists_eq_succ_of_ne_zero HEq
    rw [hk, add_comm, ← sub_le_iff_le_add]
    have := not_mem_of_lt_hitting (hk.symm ▸ k.lt_succ_self : k < least_ge f r i ω) (zero_le _)
    simp only [Set.mem_union, Set.mem_Iic, Set.mem_Ici, not_or, not_le] at this
    exact (sub_lt_sub_left this _).le.trans ((le_abs_self _).trans (hbddω _))
#align measure_theory.norm_stopped_value_least_ge_le MeasureTheory.norm_stoppedValue_leastGe_le

theorem Submartingale.stoppedValue_leastGe_snorm_le [IsFiniteMeasure μ] (hf : Submartingale f ℱ μ)
    (hr : 0 ≤ r) (hf0 : f 0 = 0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) (i : ℕ) :
    snorm (stoppedValue f (leastGe f r i)) 1 μ ≤ 2 * μ Set.univ * ENNReal.ofReal (r + R) :=
  by
  refine'
    snorm_one_le_of_le' ((hf.stopped_value_least_ge r).Integrable _) _
      (norm_stopped_value_least_ge_le hr hf0 hbdd i)
  rw [← integral_univ]
  refine' le_trans _ ((hf.stopped_value_least_ge r).set_integral_le (zero_le _) MeasurableSet.univ)
  simp_rw [stopped_value, least_ge, hitting_of_le le_rfl, hf0, integral_zero']
#align measure_theory.submartingale.stopped_value_least_ge_snorm_le MeasureTheory.Submartingale.stoppedValue_leastGe_snorm_le

theorem Submartingale.stoppedValue_leastGe_snorm_le' [IsFiniteMeasure μ] (hf : Submartingale f ℱ μ)
    (hr : 0 ≤ r) (hf0 : f 0 = 0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) (i : ℕ) :
    snorm (stoppedValue f (leastGe f r i)) 1 μ ≤
      ENNReal.toNNReal (2 * μ Set.univ * ENNReal.ofReal (r + R)) :=
  by
  refine' (hf.stopped_value_least_ge_snorm_le hr hf0 hbdd i).trans _
  simp [ENNReal.coe_toNNReal (measure_ne_top μ _), ENNReal.coe_toNNReal]
#align measure_theory.submartingale.stopped_value_least_ge_snorm_le' MeasureTheory.Submartingale.stoppedValue_leastGe_snorm_le'

/-- This lemma is superceded by `submartingale.bdd_above_iff_exists_tendsto`. -/
theorem Submartingale.exists_tendsto_of_abs_bddAbove_aux [IsFiniteMeasure μ]
    (hf : Submartingale f ℱ μ) (hf0 : f 0 = 0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
    ∀ᵐ ω ∂μ, BddAbove (Set.range fun n => f n ω) → ∃ c, Tendsto (fun n => f n ω) atTop (𝓝 c) :=
  by
  have ht :
    ∀ᵐ ω ∂μ, ∀ i : ℕ, ∃ c, tendsto (fun n => stopped_value f (least_ge f i n) ω) at_top (𝓝 c) :=
    by
    rw [ae_all_iff]
    exact fun i =>
      submartingale.exists_ae_tendsto_of_bdd (hf.stopped_value_least_ge i)
        (hf.stopped_value_least_ge_snorm_le' i.cast_nonneg hf0 hbdd)
  filter_upwards [ht]with ω hω hωb
  rw [BddAbove] at hωb
  obtain ⟨i, hi⟩ := exists_nat_gt hωb.some
  have hib : ∀ n, f n ω < i := by
    intro n
    exact lt_of_le_of_lt ((mem_upperBounds.1 hωb.some_mem) _ ⟨n, rfl⟩) hi
  have heq : ∀ n, stopped_value f (least_ge f i n) ω = f n ω :=
    by
    intro n
    rw [least_ge, hitting, stopped_value]
    simp only
    rw [if_neg]
    simp only [Set.mem_Icc, Set.mem_union, Set.mem_Ici]
    push_neg
    exact fun j _ => hib j
  simp only [← HEq, hω i]
#align measure_theory.submartingale.exists_tendsto_of_abs_bdd_above_aux MeasureTheory.Submartingale.exists_tendsto_of_abs_bddAbove_aux

theorem Submartingale.bddAbove_iff_exists_tendsto_aux [IsFiniteMeasure μ] (hf : Submartingale f ℱ μ)
    (hf0 : f 0 = 0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
    ∀ᵐ ω ∂μ, BddAbove (Set.range fun n => f n ω) ↔ ∃ c, Tendsto (fun n => f n ω) atTop (𝓝 c) := by
  filter_upwards [hf.exists_tendsto_of_abs_bdd_above_aux hf0
      hbdd]with ω hω using⟨hω, fun ⟨c, hc⟩ => hc.bddAbove_range⟩
#align measure_theory.submartingale.bdd_above_iff_exists_tendsto_aux MeasureTheory.Submartingale.bddAbove_iff_exists_tendsto_aux

/-- One sided martingale bound: If `f` is a submartingale which has uniformly bounded differences,
then for almost every `ω`, `f n ω` is bounded above (in `n`) if and only if it converges. -/
theorem Submartingale.bddAbove_iff_exists_tendsto [IsFiniteMeasure μ] (hf : Submartingale f ℱ μ)
    (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
    ∀ᵐ ω ∂μ, BddAbove (Set.range fun n => f n ω) ↔ ∃ c, Tendsto (fun n => f n ω) atTop (𝓝 c) :=
  by
  set g : ℕ → Ω → ℝ := fun n ω => f n ω - f 0 ω with hgdef
  have hg : submartingale g ℱ μ :=
    hf.sub_martingale (martingale_const_fun _ _ (hf.adapted 0) (hf.integrable 0))
  have hg0 : g 0 = 0 := by
    ext ω
    simp only [hgdef, sub_self, Pi.zero_apply]
  have hgbdd : ∀ᵐ ω ∂μ, ∀ i : ℕ, |g (i + 1) ω - g i ω| ≤ ↑R := by
    simpa only [sub_sub_sub_cancel_right]
  filter_upwards [hg.bdd_above_iff_exists_tendsto_aux hg0 hgbdd]with ω hω
  convert hω using 1 <;> rw [eq_iff_iff]
  · simp only [hgdef]
    refine' ⟨fun h => _, fun h => _⟩ <;> obtain ⟨b, hb⟩ := h <;>
        refine' ⟨b + |f 0 ω|, fun y hy => _⟩ <;>
      obtain ⟨n, rfl⟩ := hy
    · simp_rw [sub_eq_add_neg]
      exact add_le_add (hb ⟨n, rfl⟩) (neg_le_abs_self _)
    · exact sub_le_iff_le_add.1 (le_trans (sub_le_sub_left (le_abs_self _) _) (hb ⟨n, rfl⟩))
  · simp only [hgdef]
    refine' ⟨fun h => _, fun h => _⟩ <;> obtain ⟨c, hc⟩ := h
    · exact ⟨c - f 0 ω, hc.sub_const _⟩
    · refine' ⟨c + f 0 ω, _⟩
      have := hc.add_const (f 0 ω)
      simpa only [sub_add_cancel]
#align measure_theory.submartingale.bdd_above_iff_exists_tendsto MeasureTheory.Submartingale.bddAbove_iff_exists_tendsto

/-!
### Lévy's generalization of the Borel-Cantelli lemma

Lévy's generalization of the Borel-Cantelli lemma states that: given a natural number indexed
filtration $(\mathcal{F}_n)$, and a sequence of sets $(s_n)$ such that for all
$n$, $s_n \in \mathcal{F}_n$, $limsup_n s_n$ is almost everywhere equal to the set for which
$\sum_n \mathbb{P}[s_n \mid \mathcal{F}_n] = \infty$.

The proof strategy follows by constructing a martingale satisfying the one sided martingale bound.
In particular, we define
$$
  f_n := \sum_{k < n} \mathbf{1}_{s_{n + 1}} - \mathbb{P}[s_{n + 1} \mid \mathcal{F}_n].
$$
Then, as a martingale is both a sub and a super-martingale, the set for which it is unbounded from
above must agree with the set for which it is unbounded from below almost everywhere. Thus, it
can only converge to $\pm \infty$ with probability 0. Thus, by considering
$$
  \limsup_n s_n = \{\sum_n \mathbf{1}_{s_n} = \infty\}
$$
almost everywhere, the result follows.
-/

theorem Martingale.bddAbove_range_iff_bddBelow_range [IsFiniteMeasure μ] (hf : Martingale f ℱ μ)
    (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
    ∀ᵐ ω ∂μ, BddAbove (Set.range fun n => f n ω) ↔ BddBelow (Set.range fun n => f n ω) :=
  by
  have hbdd' : ∀ᵐ ω ∂μ, ∀ i, |(-f) (i + 1) ω - (-f) i ω| ≤ R :=
    by
    filter_upwards [hbdd]with ω hω i
    erw [← abs_neg, neg_sub, sub_neg_eq_add, neg_add_eq_sub]
    exact hω i
  have hup := hf.submartingale.bdd_above_iff_exists_tendsto hbdd
  have hdown := hf.neg.submartingale.bdd_above_iff_exists_tendsto hbdd'
  filter_upwards [hup, hdown]with ω hω₁ hω₂
  have :
    (∃ c, tendsto (fun n => f n ω) at_top (𝓝 c)) ↔ ∃ c, tendsto (fun n => (-f) n ω) at_top (𝓝 c) :=
    by
    constructor <;> rintro ⟨c, hc⟩
    · exact ⟨-c, hc.neg⟩
    · refine' ⟨-c, _⟩
      convert hc.neg
      simp only [neg_neg, Pi.neg_apply]
  rw [hω₁, this, ← hω₂]
  constructor <;> rintro ⟨c, hc⟩ <;> refine' ⟨-c, fun ω hω => _⟩
  · rw [mem_upperBounds] at hc
    refine' neg_le.2 (hc _ _)
    simpa only [Pi.neg_apply, Set.mem_range, neg_inj]
  · rw [mem_lowerBounds] at hc
    simp_rw [Set.mem_range, Pi.neg_apply, neg_eq_iff_eq_neg] at hω
    refine' le_neg.1 (hc _ _)
    simpa only [Set.mem_range]
#align measure_theory.martingale.bdd_above_range_iff_bdd_below_range MeasureTheory.Martingale.bddAbove_range_iff_bddBelow_range

theorem Martingale.ae_not_tendsto_atTop_atTop [IsFiniteMeasure μ] (hf : Martingale f ℱ μ)
    (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
    ∀ᵐ ω ∂μ, ¬Tendsto (fun n => f n ω) atTop atTop := by
  filter_upwards [hf.bdd_above_range_iff_bdd_below_range
      hbdd]with ω hω htop using unbounded_of_tendsto_at_top htop
      (hω.2 <| bddBelow_range_of_tendsto_atTop_atTop htop)
#align measure_theory.martingale.ae_not_tendsto_at_top_at_top MeasureTheory.Martingale.ae_not_tendsto_atTop_atTop

theorem Martingale.ae_not_tendsto_atTop_atBot [IsFiniteMeasure μ] (hf : Martingale f ℱ μ)
    (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) :
    ∀ᵐ ω ∂μ, ¬Tendsto (fun n => f n ω) atTop atBot := by
  filter_upwards [hf.bdd_above_range_iff_bdd_below_range
      hbdd]with ω hω htop using unbounded_of_tendsto_at_bot htop
      (hω.1 <| bddAbove_range_of_tendsto_atTop_atBot htop)
#align measure_theory.martingale.ae_not_tendsto_at_top_at_bot MeasureTheory.Martingale.ae_not_tendsto_atTop_atBot

namespace BorelCantelli

/-- Auxiliary definition required to prove Lévy's generalization of the Borel-Cantelli lemmas for
which we will take the martingale part. -/
noncomputable def process (s : ℕ → Set Ω) (n : ℕ) : Ω → ℝ :=
  ∑ k in Finset.range n, (s (k + 1)).indicator 1
#align measure_theory.borel_cantelli.process MeasureTheory.BorelCantelli.process

variable {s : ℕ → Set Ω}

theorem process_zero : process s 0 = 0 := by rw [process, Finset.range_zero, Finset.sum_empty]
#align measure_theory.borel_cantelli.process_zero MeasureTheory.BorelCantelli.process_zero

theorem adapted_process (hs : ∀ n, measurable_set[ℱ n] (s n)) : Adapted ℱ (process s) := fun n =>
  Finset.stronglyMeasurable_sum' _ fun k hk =>
    stronglyMeasurable_one.indicator <| ℱ.mono (Finset.mem_range.1 hk) _ <| hs _
#align measure_theory.borel_cantelli.adapted_process MeasureTheory.BorelCantelli.adapted_process

theorem martingalePart_process_ae_eq (ℱ : Filtration ℕ m0) (μ : Measure Ω) (s : ℕ → Set Ω) (n : ℕ) :
    martingalePart (process s) ℱ μ n =
      ∑ k in Finset.range n, (s (k + 1)).indicator 1 - μ[(s (k + 1)).indicator 1|ℱ k] :=
  by
  simp only [martingale_part_eq_sum, process_zero, zero_add]
  refine' Finset.sum_congr rfl fun k hk => _
  simp only [process, Finset.sum_range_succ_sub_sum]
#align measure_theory.borel_cantelli.martingale_part_process_ae_eq MeasureTheory.BorelCantelli.martingalePart_process_ae_eq

theorem predictablePart_process_ae_eq (ℱ : Filtration ℕ m0) (μ : Measure Ω) (s : ℕ → Set Ω)
    (n : ℕ) :
    predictablePart (process s) ℱ μ n =
      ∑ k in Finset.range n, μ[(s (k + 1)).indicator (1 : Ω → ℝ)|ℱ k] :=
  by
  have := martingale_part_process_ae_eq ℱ μ s n
  simp_rw [martingale_part, process, Finset.sum_sub_distrib] at this
  exact sub_right_injective this
#align measure_theory.borel_cantelli.predictable_part_process_ae_eq MeasureTheory.BorelCantelli.predictablePart_process_ae_eq

theorem process_difference_le (s : ℕ → Set Ω) (ω : Ω) (n : ℕ) :
    |process s (n + 1) ω - process s n ω| ≤ (1 : ℝ≥0) :=
  by
  rw [Nonneg.coe_one, process, process, Finset.sum_apply, Finset.sum_apply,
    Finset.sum_range_succ_sub_sum, ← Real.norm_eq_abs, norm_indicator_eq_indicator_norm]
  refine' Set.indicator_le' (fun _ _ => _) (fun _ _ => zero_le_one) _
  rw [Pi.one_apply, norm_one]
#align measure_theory.borel_cantelli.process_difference_le MeasureTheory.BorelCantelli.process_difference_le

theorem integrableProcess (μ : Measure Ω) [IsFiniteMeasure μ] (hs : ∀ n, measurable_set[ℱ n] (s n))
    (n : ℕ) : Integrable (process s n) μ :=
  integrableFinsetSum' _ fun k hk =>
    IntegrableOn.integrableIndicator (integrableConst 1) <| ℱ.le _ _ <| hs _
#align measure_theory.borel_cantelli.integrable_process MeasureTheory.BorelCantelli.integrableProcess

end BorelCantelli

open BorelCantelli

/-- An a.e. monotone adapted process `f` with uniformly bounded differences converges to `+∞` if
and only if its predictable part also converges to `+∞`. -/
theorem tendsto_sum_indicator_atTop_iff [IsFiniteMeasure μ]
    (hfmono : ∀ᵐ ω ∂μ, ∀ n, f n ω ≤ f (n + 1) ω) (hf : Adapted ℱ f) (hint : ∀ n, Integrable (f n) μ)
    (hbdd : ∀ᵐ ω ∂μ, ∀ n, |f (n + 1) ω - f n ω| ≤ R) :
    ∀ᵐ ω ∂μ,
      Tendsto (fun n => f n ω) atTop atTop ↔
        Tendsto (fun n => predictablePart f ℱ μ n ω) atTop atTop :=
  by
  have h₁ :=
    (martingale_martingale_part hf hint).ae_not_tendsto_atTop_atTop
      (martingale_part_bdd_difference ℱ hbdd)
  have h₂ :=
    (martingale_martingale_part hf hint).ae_not_tendsto_atTop_atBot
      (martingale_part_bdd_difference ℱ hbdd)
  have h₃ : ∀ᵐ ω ∂μ, ∀ n, 0 ≤ (μ[f (n + 1) - f n|ℱ n]) ω :=
    by
    refine' ae_all_iff.2 fun n => condexp_nonneg _
    filter_upwards [ae_all_iff.1 hfmono n]with ω hω using sub_nonneg.2 hω
  filter_upwards [h₁, h₂, h₃, hfmono]with ω hω₁ hω₂ hω₃ hω₄
  constructor <;> intro ht
  · refine' tendsto_at_top_at_top_of_monotone' _ _
    · intro n m hnm
      simp only [predictable_part, Finset.sum_apply]
      refine' Finset.sum_mono_set_of_nonneg hω₃ (Finset.range_mono hnm)
    rintro ⟨b, hbdd⟩
    rw [← tendsto_neg_at_bot_iff] at ht
    simp only [martingale_part, sub_eq_add_neg] at hω₁
    exact
      hω₁
        (tendsto_at_top_add_right_of_le _ (-b) (tendsto_neg_at_bot_iff.1 ht) fun n =>
          neg_le_neg (hbdd ⟨n, rfl⟩))
  · refine' tendsto_at_top_at_top_of_monotone' (monotone_nat_of_le_succ hω₄) _
    rintro ⟨b, hbdd⟩
    exact
      hω₂
        ((tendsto_at_bot_add_left_of_ge _ b fun n => hbdd ⟨n, rfl⟩) <| tendsto_neg_at_bot_iff.2 ht)
#align measure_theory.tendsto_sum_indicator_at_top_iff MeasureTheory.tendsto_sum_indicator_atTop_iff

open BorelCantelli

theorem tendsto_sum_indicator_atTop_iff' [IsFiniteMeasure μ] {s : ℕ → Set Ω}
    (hs : ∀ n, measurable_set[ℱ n] (s n)) :
    ∀ᵐ ω ∂μ,
      Tendsto (fun n => ∑ k in Finset.range n, (s (k + 1)).indicator (1 : Ω → ℝ) ω) atTop atTop ↔
        Tendsto (fun n => ∑ k in Finset.range n, (μ[(s (k + 1)).indicator (1 : Ω → ℝ)|ℱ k]) ω) atTop
          atTop :=
  by
  have :=
    tendsto_sum_indicator_at_top_iff (eventually_of_forall fun ω n => _) (adapted_process hs)
      (integrable_process μ hs) (eventually_of_forall <| process_difference_le s)
  swap
  · rw [process, process, ← sub_nonneg, Finset.sum_apply, Finset.sum_apply,
      Finset.sum_range_succ_sub_sum]
    exact Set.indicator_nonneg (fun _ _ => zero_le_one) _
  simp_rw [process, predictable_part_process_ae_eq] at this
  simpa using this
#align measure_theory.tendsto_sum_indicator_at_top_iff' MeasureTheory.tendsto_sum_indicator_atTop_iff'

/-- **Lévy's generalization of the Borel-Cantelli lemma**: given a sequence of sets `s` and a
filtration `ℱ` such that for all `n`, `s n` is `ℱ n`-measurable, `at_top.limsup s` is almost
everywhere equal to the set for which `∑ k, ℙ(s (k + 1) | ℱ k) = ∞`. -/
theorem ae_mem_limsup_atTop_iff (μ : Measure Ω) [IsFiniteMeasure μ] {s : ℕ → Set Ω}
    (hs : ∀ n, measurable_set[ℱ n] (s n)) :
    ∀ᵐ ω ∂μ,
      ω ∈ limsup s atTop ↔
        Tendsto (fun n => ∑ k in Finset.range n, (μ[(s (k + 1)).indicator (1 : Ω → ℝ)|ℱ k]) ω) atTop
          atTop :=
  (limsup_eq_tendsto_sum_indicator_atTop ℝ s).symm ▸ tendsto_sum_indicator_atTop_iff' hs
#align measure_theory.ae_mem_limsup_at_top_iff MeasureTheory.ae_mem_limsup_atTop_iff

end MeasureTheory


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.multiset.powerset
! leanprover-community/mathlib commit f2f413b9d4be3a02840d0663dace76e8fe3da053
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.Sublists
import Mathbin.Data.Multiset.Nodup

/-!
# The powerset of a multiset

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

namespace Multiset

open List

variable {α : Type _}

/-! ### powerset -/

#print Multiset.powersetAux /-
/-- A helper function for the powerset of a multiset. Given a list `l`, returns a list
of sublists of `l` (using `sublists_aux`), as multisets. -/
def powersetAux (l : List α) : List (Multiset α) :=
  0 :: sublistsAux l fun x y => x :: y
#align multiset.powerset_aux Multiset.powersetAux
-/

#print Multiset.powersetAux_eq_map_coe /-
theorem powersetAux_eq_map_coe {l : List α} : powersetAux l = (sublists l).map coe := by
  simp [powerset_aux, sublists] <;>
      rw [←
        show (@sublists_aux₁ α (Multiset α) l fun x => [↑x]) = sublists_aux l fun x => List.cons ↑x
          from sublists_aux₁_eq_sublists_aux _ _,
        sublists_aux_cons_eq_sublists_aux₁, ← bind_ret_eq_map, sublists_aux₁_bind] <;>
    rfl
#align multiset.powerset_aux_eq_map_coe Multiset.powersetAux_eq_map_coe
-/

/- warning: multiset.mem_powerset_aux -> Multiset.mem_powersetAux is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : List.{u1} α} {s : Multiset.{u1} α}, Iff (Membership.Mem.{u1, u1} (Multiset.{u1} α) (List.{u1} (Multiset.{u1} α)) (List.hasMem.{u1} (Multiset.{u1} α)) s (Multiset.powersetAux.{u1} α l)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (List.{u1} α) (Multiset.{u1} α) (HasLiftT.mk.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (coeBase.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (Multiset.hasCoe.{u1} α)))) l))
but is expected to have type
  forall {α : Type.{u1}} {l : List.{u1} α} {s : Multiset.{u1} α}, Iff (Membership.mem.{u1, u1} (Multiset.{u1} α) (List.{u1} (Multiset.{u1} α)) (List.instMembershipList.{u1} (Multiset.{u1} α)) s (Multiset.powersetAux.{u1} α l)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s (Multiset.ofList.{u1} α l))
Case conversion may be inaccurate. Consider using '#align multiset.mem_powerset_aux Multiset.mem_powersetAuxₓ'. -/
@[simp]
theorem mem_powersetAux {l : List α} {s} : s ∈ powersetAux l ↔ s ≤ ↑l :=
  Quotient.inductionOn s <| by simp [powerset_aux_eq_map_coe, subperm, and_comm]
#align multiset.mem_powerset_aux Multiset.mem_powersetAux

#print Multiset.powersetAux' /-
/-- Helper function for the powerset of a multiset. Given a list `l`, returns a list
of sublists of `l` (using `sublists'`), as multisets. -/
def powersetAux' (l : List α) : List (Multiset α) :=
  (sublists' l).map coe
#align multiset.powerset_aux' Multiset.powersetAux'
-/

#print Multiset.powersetAux_perm_powersetAux' /-
theorem powersetAux_perm_powersetAux' {l : List α} : powersetAux l ~ powersetAux' l := by
  rw [powerset_aux_eq_map_coe] <;> exact (sublists_perm_sublists' _).map _
#align multiset.powerset_aux_perm_powerset_aux' Multiset.powersetAux_perm_powersetAux'
-/

#print Multiset.powersetAux'_nil /-
@[simp]
theorem powersetAux'_nil : powersetAux' (@nil α) = [0] :=
  rfl
#align multiset.powerset_aux'_nil Multiset.powersetAux'_nil
-/

#print Multiset.powersetAux'_cons /-
@[simp]
theorem powersetAux'_cons (a : α) (l : List α) :
    powersetAux' (a :: l) = powersetAux' l ++ List.map (cons a) (powersetAux' l) := by
  simp [powerset_aux'] <;> rfl
#align multiset.powerset_aux'_cons Multiset.powersetAux'_cons
-/

#print Multiset.powerset_aux'_perm /-
theorem powerset_aux'_perm {l₁ l₂ : List α} (p : l₁ ~ l₂) : powersetAux' l₁ ~ powersetAux' l₂ :=
  by
  induction' p with a l₁ l₂ p IH a b l l₁ l₂ l₃ p₁ p₂ IH₁ IH₂; · simp
  · simp
    exact IH.append (IH.map _)
  · simp
    apply perm.append_left
    rw [← append_assoc, ← append_assoc,
      (by funext s <;> simp [cons_swap] : cons b ∘ cons a = cons a ∘ cons b)]
    exact perm_append_comm.append_right _
  · exact IH₁.trans IH₂
#align multiset.powerset_aux'_perm Multiset.powerset_aux'_perm
-/

#print Multiset.powersetAux_perm /-
theorem powersetAux_perm {l₁ l₂ : List α} (p : l₁ ~ l₂) : powersetAux l₁ ~ powersetAux l₂ :=
  powersetAux_perm_powersetAux'.trans <|
    (powerset_aux'_perm p).trans powersetAux_perm_powersetAux'.symm
#align multiset.powerset_aux_perm Multiset.powersetAux_perm
-/

#print Multiset.powerset /-
/-- The power set of a multiset. -/
def powerset (s : Multiset α) : Multiset (Multiset α) :=
  Quot.liftOn s (fun l => (powersetAux l : Multiset (Multiset α))) fun l₁ l₂ h =>
    Quot.sound (powersetAux_perm h)
#align multiset.powerset Multiset.powerset
-/

#print Multiset.powerset_coe /-
theorem powerset_coe (l : List α) : @powerset α l = ((sublists l).map coe : List (Multiset α)) :=
  congr_arg coe powersetAux_eq_map_coe
#align multiset.powerset_coe Multiset.powerset_coe
-/

#print Multiset.powerset_coe' /-
@[simp]
theorem powerset_coe' (l : List α) : @powerset α l = ((sublists' l).map coe : List (Multiset α)) :=
  Quot.sound powersetAux_perm_powersetAux'
#align multiset.powerset_coe' Multiset.powerset_coe'
-/

#print Multiset.powerset_zero /-
@[simp]
theorem powerset_zero : @powerset α 0 = {0} :=
  rfl
#align multiset.powerset_zero Multiset.powerset_zero
-/

#print Multiset.powerset_cons /-
@[simp]
theorem powerset_cons (a : α) (s) : powerset (a ::ₘ s) = powerset s + map (cons a) (powerset s) :=
  Quotient.inductionOn s fun l => by simp <;> rfl
#align multiset.powerset_cons Multiset.powerset_cons
-/

/- warning: multiset.mem_powerset -> Multiset.mem_powerset is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, Iff (Membership.Mem.{u1, u1} (Multiset.{u1} α) (Multiset.{u1} (Multiset.{u1} α)) (Multiset.hasMem.{u1} (Multiset.{u1} α)) s (Multiset.powerset.{u1} α t)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t)
but is expected to have type
  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, Iff (Membership.mem.{u1, u1} (Multiset.{u1} α) (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instMembershipMultiset.{u1} (Multiset.{u1} α)) s (Multiset.powerset.{u1} α t)) (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t)
Case conversion may be inaccurate. Consider using '#align multiset.mem_powerset Multiset.mem_powersetₓ'. -/
@[simp]
theorem mem_powerset {s t : Multiset α} : s ∈ powerset t ↔ s ≤ t :=
  Quotient.induction_on₂ s t <| by simp [subperm, and_comm]
#align multiset.mem_powerset Multiset.mem_powerset

/- warning: multiset.map_single_le_powerset -> Multiset.map_single_le_powerset is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Multiset.{u1} α), LE.le.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Preorder.toLE.{u1} (Multiset.{u1} (Multiset.{u1} α)) (PartialOrder.toPreorder.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.partialOrder.{u1} (Multiset.{u1} α)))) (Multiset.map.{u1, u1} α (Multiset.{u1} α) (Singleton.singleton.{u1, u1} α (Multiset.{u1} α) (Multiset.hasSingleton.{u1} α)) s) (Multiset.powerset.{u1} α s)
but is expected to have type
  forall {α : Type.{u1}} (s : Multiset.{u1} α), LE.le.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Preorder.toLE.{u1} (Multiset.{u1} (Multiset.{u1} α)) (PartialOrder.toPreorder.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instPartialOrderMultiset.{u1} (Multiset.{u1} α)))) (Multiset.map.{u1, u1} α (Multiset.{u1} α) (Singleton.singleton.{u1, u1} α (Multiset.{u1} α) (Multiset.instSingletonMultiset.{u1} α)) s) (Multiset.powerset.{u1} α s)
Case conversion may be inaccurate. Consider using '#align multiset.map_single_le_powerset Multiset.map_single_le_powersetₓ'. -/
theorem map_single_le_powerset (s : Multiset α) : s.map singleton ≤ powerset s :=
  Quotient.inductionOn s fun l =>
    by
    simp only [powerset_coe, quot_mk_to_coe, coe_le, coe_map]
    show l.map (coe ∘ List.ret) <+~ (sublists l).map coe
    rw [← List.map_map]
    exact ((map_ret_sublist_sublists _).map _).Subperm
#align multiset.map_single_le_powerset Multiset.map_single_le_powerset

/- warning: multiset.card_powerset -> Multiset.card_powerset is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Multiset.{u1} α), Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.orderedCancelAddCommMonoid.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.orderedCancelAddCommMonoid.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} (Multiset.{u1} α)) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.orderedCancelAddCommMonoid.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} (Multiset.{u1} α)) (Multiset.powerset.{u1} α s)) (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))) (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) s))
but is expected to have type
  forall {α : Type.{u1}} (s : Multiset.{u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) (Multiset.powerset.{u1} α s)) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} (Multiset.{u1} α)) (fun (_x : Multiset.{u1} (Multiset.{u1} α)) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} (Multiset.{u1} α)) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α)))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} (Multiset.{u1} α)) (Multiset.powerset.{u1} α s)) (HPow.hPow.{0, 0, 0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) (Multiset.powerset.{u1} α s)) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) (Multiset.powerset.{u1} α s)) (instHPow.{0, 0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) (Multiset.powerset.{u1} α s)) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) instPowNat) (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) (Multiset.powerset.{u1} α s)) 2 (instOfNatNat 2)) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) s))
Case conversion may be inaccurate. Consider using '#align multiset.card_powerset Multiset.card_powersetₓ'. -/
@[simp]
theorem card_powerset (s : Multiset α) : card (powerset s) = 2 ^ card s :=
  Quotient.inductionOn s <| by simp
#align multiset.card_powerset Multiset.card_powerset

#print Multiset.revzip_powersetAux /-
theorem revzip_powersetAux {l : List α} ⦃x⦄ (h : x ∈ revzip (powersetAux l)) : x.1 + x.2 = ↑l :=
  by
  rw [revzip, powerset_aux_eq_map_coe, ← map_reverse, zip_map, ← revzip] at h
  simp at h; rcases h with ⟨l₁, l₂, h, rfl, rfl⟩
  exact Quot.sound (revzip_sublists _ _ _ h)
#align multiset.revzip_powerset_aux Multiset.revzip_powersetAux
-/

#print Multiset.revzip_powersetAux' /-
theorem revzip_powersetAux' {l : List α} ⦃x⦄ (h : x ∈ revzip (powersetAux' l)) : x.1 + x.2 = ↑l :=
  by
  rw [revzip, powerset_aux', ← map_reverse, zip_map, ← revzip] at h
  simp at h; rcases h with ⟨l₁, l₂, h, rfl, rfl⟩
  exact Quot.sound (revzip_sublists' _ _ _ h)
#align multiset.revzip_powerset_aux' Multiset.revzip_powersetAux'
-/

#print Multiset.revzip_powersetAux_lemma /-
theorem revzip_powersetAux_lemma [DecidableEq α] (l : List α) {l' : List (Multiset α)}
    (H : ∀ ⦃x : _ × _⦄, x ∈ revzip l' → x.1 + x.2 = ↑l) : revzip l' = l'.map fun x => (x, ↑l - x) :=
  by
  have :
    forall₂ (fun (p : Multiset α × Multiset α) (s : Multiset α) => p = (s, ↑l - s)) (revzip l')
      ((revzip l').map Prod.fst) :=
    by
    rw [forall₂_map_right_iff, forall₂_same]
    rintro ⟨s, t⟩ h
    dsimp
    rw [← H h, add_tsub_cancel_left]
  rw [← forall₂_eq_eq_eq, forall₂_map_right_iff]
  simpa
#align multiset.revzip_powerset_aux_lemma Multiset.revzip_powersetAux_lemma
-/

#print Multiset.revzip_powersetAux_perm_aux' /-
theorem revzip_powersetAux_perm_aux' {l : List α} :
    revzip (powersetAux l) ~ revzip (powersetAux' l) :=
  by
  haveI := Classical.decEq α
  rw [revzip_powerset_aux_lemma l revzip_powerset_aux,
    revzip_powerset_aux_lemma l revzip_powerset_aux']
  exact powerset_aux_perm_powerset_aux'.map _
#align multiset.revzip_powerset_aux_perm_aux' Multiset.revzip_powersetAux_perm_aux'
-/

#print Multiset.revzip_powersetAux_perm /-
theorem revzip_powersetAux_perm {l₁ l₂ : List α} (p : l₁ ~ l₂) :
    revzip (powersetAux l₁) ~ revzip (powersetAux l₂) :=
  by
  haveI := Classical.decEq α
  simp [fun l : List α => revzip_powerset_aux_lemma l revzip_powerset_aux, coe_eq_coe.2 p]
  exact (powerset_aux_perm p).map _
#align multiset.revzip_powerset_aux_perm Multiset.revzip_powersetAux_perm
-/

/-! ### powerset_len -/

#print Multiset.powersetLenAux /-
/-- Helper function for `powerset_len`. Given a list `l`, `powerset_len_aux n l` is the list
of sublists of length `n`, as multisets. -/
def powersetLenAux (n : ℕ) (l : List α) : List (Multiset α) :=
  sublistsLenAux n l coe []
#align multiset.powerset_len_aux Multiset.powersetLenAux
-/

#print Multiset.powersetLenAux_eq_map_coe /-
theorem powersetLenAux_eq_map_coe {n} {l : List α} :
    powersetLenAux n l = (sublistsLen n l).map coe := by
  rw [powerset_len_aux, sublists_len_aux_eq, append_nil]
#align multiset.powerset_len_aux_eq_map_coe Multiset.powersetLenAux_eq_map_coe
-/

/- warning: multiset.mem_powerset_len_aux -> Multiset.mem_powersetLenAux is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n : Nat} {l : List.{u1} α} {s : Multiset.{u1} α}, Iff (Membership.Mem.{u1, u1} (Multiset.{u1} α) (List.{u1} (Multiset.{u1} α)) (List.hasMem.{u1} (Multiset.{u1} α)) s (Multiset.powersetLenAux.{u1} α n l)) (And (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (List.{u1} α) (Multiset.{u1} α) (HasLiftT.mk.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (coeBase.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (Multiset.hasCoe.{u1} α)))) l)) (Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) s) n))
but is expected to have type
  forall {α : Type.{u1}} {n : Nat} {l : List.{u1} α} {s : Multiset.{u1} α}, Iff (Membership.mem.{u1, u1} (Multiset.{u1} α) (List.{u1} (Multiset.{u1} α)) (List.instMembershipList.{u1} (Multiset.{u1} α)) s (Multiset.powersetLenAux.{u1} α n l)) (And (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s (Multiset.ofList.{u1} α l)) (Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) s) n))
Case conversion may be inaccurate. Consider using '#align multiset.mem_powerset_len_aux Multiset.mem_powersetLenAuxₓ'. -/
@[simp]
theorem mem_powersetLenAux {n} {l : List α} {s} : s ∈ powersetLenAux n l ↔ s ≤ ↑l ∧ card s = n :=
  Quotient.inductionOn s <| by
    simp [powerset_len_aux_eq_map_coe, subperm] <;>
      exact fun l₁ =>
        ⟨fun ⟨l₂, ⟨s, e⟩, p⟩ => ⟨⟨_, p, s⟩, p.symm.length_eq.trans e⟩, fun ⟨⟨l₂, p, s⟩, e⟩ =>
          ⟨_, ⟨s, p.length_eq.trans e⟩, p⟩⟩
#align multiset.mem_powerset_len_aux Multiset.mem_powersetLenAux

#print Multiset.powersetLenAux_zero /-
@[simp]
theorem powersetLenAux_zero (l : List α) : powersetLenAux 0 l = [0] := by
  simp [powerset_len_aux_eq_map_coe]
#align multiset.powerset_len_aux_zero Multiset.powersetLenAux_zero
-/

#print Multiset.powersetLenAux_nil /-
@[simp]
theorem powersetLenAux_nil (n : ℕ) : powersetLenAux (n + 1) (@nil α) = [] :=
  rfl
#align multiset.powerset_len_aux_nil Multiset.powersetLenAux_nil
-/

#print Multiset.powersetLenAux_cons /-
@[simp]
theorem powersetLenAux_cons (n : ℕ) (a : α) (l : List α) :
    powersetLenAux (n + 1) (a :: l) =
      powersetLenAux (n + 1) l ++ List.map (cons a) (powersetLenAux n l) :=
  by simp [powerset_len_aux_eq_map_coe] <;> rfl
#align multiset.powerset_len_aux_cons Multiset.powersetLenAux_cons
-/

#print Multiset.powersetLenAux_perm /-
theorem powersetLenAux_perm {n} {l₁ l₂ : List α} (p : l₁ ~ l₂) :
    powersetLenAux n l₁ ~ powersetLenAux n l₂ :=
  by
  induction' n with n IHn generalizing l₁ l₂; · simp
  induction' p with a l₁ l₂ p IH a b l l₁ l₂ l₃ p₁ p₂ IH₁ IH₂; · rfl
  · simp
    exact IH.append ((IHn p).map _)
  · simp
    apply perm.append_left
    cases n
    · simp
      apply perm.swap
    simp
    rw [← append_assoc, ← append_assoc,
      (by funext s <;> simp [cons_swap] : cons b ∘ cons a = cons a ∘ cons b)]
    exact perm_append_comm.append_right _
  · exact IH₁.trans IH₂
#align multiset.powerset_len_aux_perm Multiset.powersetLenAux_perm
-/

#print Multiset.powersetLen /-
/-- `powerset_len n s` is the multiset of all submultisets of `s` of length `n`. -/
def powersetLen (n : ℕ) (s : Multiset α) : Multiset (Multiset α) :=
  Quot.liftOn s (fun l => (powersetLenAux n l : Multiset (Multiset α))) fun l₁ l₂ h =>
    Quot.sound (powersetLenAux_perm h)
#align multiset.powerset_len Multiset.powersetLen
-/

#print Multiset.powersetLen_coe' /-
theorem powersetLen_coe' (n) (l : List α) : @powersetLen α n l = powersetLenAux n l :=
  rfl
#align multiset.powerset_len_coe' Multiset.powersetLen_coe'
-/

#print Multiset.powersetLen_coe /-
theorem powersetLen_coe (n) (l : List α) :
    @powersetLen α n l = ((sublistsLen n l).map coe : List (Multiset α)) :=
  congr_arg coe powersetLenAux_eq_map_coe
#align multiset.powerset_len_coe Multiset.powersetLen_coe
-/

#print Multiset.powersetLen_zero_left /-
@[simp]
theorem powersetLen_zero_left (s : Multiset α) : powersetLen 0 s = {0} :=
  Quotient.inductionOn s fun l => by simp [powerset_len_coe'] <;> rfl
#align multiset.powerset_len_zero_left Multiset.powersetLen_zero_left
-/

#print Multiset.powersetLen_zero_right /-
theorem powersetLen_zero_right (n : ℕ) : @powersetLen α (n + 1) 0 = 0 :=
  rfl
#align multiset.powerset_len_zero_right Multiset.powersetLen_zero_right
-/

#print Multiset.powersetLen_cons /-
@[simp]
theorem powersetLen_cons (n : ℕ) (a : α) (s) :
    powersetLen (n + 1) (a ::ₘ s) = powersetLen (n + 1) s + map (cons a) (powersetLen n s) :=
  Quotient.inductionOn s fun l => by simp [powerset_len_coe'] <;> rfl
#align multiset.powerset_len_cons Multiset.powersetLen_cons
-/

/- warning: multiset.mem_powerset_len -> Multiset.mem_powersetLen is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n : Nat} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, Iff (Membership.Mem.{u1, u1} (Multiset.{u1} α) (Multiset.{u1} (Multiset.{u1} α)) (Multiset.hasMem.{u1} (Multiset.{u1} α)) s (Multiset.powersetLen.{u1} α n t)) (And (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) (Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) s) n))
but is expected to have type
  forall {α : Type.{u1}} {n : Nat} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, Iff (Membership.mem.{u1, u1} (Multiset.{u1} α) (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instMembershipMultiset.{u1} (Multiset.{u1} α)) s (Multiset.powersetLen.{u1} α n t)) (And (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) (Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) s) n))
Case conversion may be inaccurate. Consider using '#align multiset.mem_powerset_len Multiset.mem_powersetLenₓ'. -/
@[simp]
theorem mem_powersetLen {n : ℕ} {s t : Multiset α} : s ∈ powersetLen n t ↔ s ≤ t ∧ card s = n :=
  Quotient.inductionOn t fun l => by simp [powerset_len_coe']
#align multiset.mem_powerset_len Multiset.mem_powersetLen

/- warning: multiset.card_powerset_len -> Multiset.card_powersetLen is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (n : Nat) (s : Multiset.{u1} α), Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.orderedCancelAddCommMonoid.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.orderedCancelAddCommMonoid.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} (Multiset.{u1} α)) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.orderedCancelAddCommMonoid.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} (Multiset.{u1} α)) (Multiset.powersetLen.{u1} α n s)) (Nat.choose (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) s) n)
but is expected to have type
  forall {α : Type.{u1}} (n : Nat) (s : Multiset.{u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) (Multiset.powersetLen.{u1} α n s)) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} (Multiset.{u1} α)) (fun (_x : Multiset.{u1} (Multiset.{u1} α)) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} (Multiset.{u1} α)) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} (Multiset.{u1} α)) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α)))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} (Multiset.{u1} α)) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} (Multiset.{u1} α))))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} (Multiset.{u1} α)) (Multiset.powersetLen.{u1} α n s)) (Nat.choose (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) s) n)
Case conversion may be inaccurate. Consider using '#align multiset.card_powerset_len Multiset.card_powersetLenₓ'. -/
@[simp]
theorem card_powersetLen (n : ℕ) (s : Multiset α) :
    card (powersetLen n s) = Nat.choose (card s) n :=
  Quotient.inductionOn s <| by simp [powerset_len_coe]
#align multiset.card_powerset_len Multiset.card_powersetLen

/- warning: multiset.powerset_len_le_powerset -> Multiset.powersetLen_le_powerset is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (n : Nat) (s : Multiset.{u1} α), LE.le.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Preorder.toLE.{u1} (Multiset.{u1} (Multiset.{u1} α)) (PartialOrder.toPreorder.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.partialOrder.{u1} (Multiset.{u1} α)))) (Multiset.powersetLen.{u1} α n s) (Multiset.powerset.{u1} α s)
but is expected to have type
  forall {α : Type.{u1}} (n : Nat) (s : Multiset.{u1} α), LE.le.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Preorder.toLE.{u1} (Multiset.{u1} (Multiset.{u1} α)) (PartialOrder.toPreorder.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instPartialOrderMultiset.{u1} (Multiset.{u1} α)))) (Multiset.powersetLen.{u1} α n s) (Multiset.powerset.{u1} α s)
Case conversion may be inaccurate. Consider using '#align multiset.powerset_len_le_powerset Multiset.powersetLen_le_powersetₓ'. -/
theorem powersetLen_le_powerset (n : ℕ) (s : Multiset α) : powersetLen n s ≤ powerset s :=
  Quotient.inductionOn s fun l => by
    simp [powerset_len_coe] <;> exact ((sublists_len_sublist_sublists' _ _).map _).Subperm
#align multiset.powerset_len_le_powerset Multiset.powersetLen_le_powerset

/- warning: multiset.powerset_len_mono -> Multiset.powersetLen_mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (n : Nat) {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) -> (LE.le.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Preorder.toLE.{u1} (Multiset.{u1} (Multiset.{u1} α)) (PartialOrder.toPreorder.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.partialOrder.{u1} (Multiset.{u1} α)))) (Multiset.powersetLen.{u1} α n s) (Multiset.powersetLen.{u1} α n t))
but is expected to have type
  forall {α : Type.{u1}} (n : Nat) {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) -> (LE.le.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Preorder.toLE.{u1} (Multiset.{u1} (Multiset.{u1} α)) (PartialOrder.toPreorder.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instPartialOrderMultiset.{u1} (Multiset.{u1} α)))) (Multiset.powersetLen.{u1} α n s) (Multiset.powersetLen.{u1} α n t))
Case conversion may be inaccurate. Consider using '#align multiset.powerset_len_mono Multiset.powersetLen_monoₓ'. -/
theorem powersetLen_mono (n : ℕ) {s t : Multiset α} (h : s ≤ t) :
    powersetLen n s ≤ powersetLen n t :=
  leInductionOn h fun l₁ l₂ h => by
    simp [powerset_len_coe] <;> exact ((sublists_len_sublist_of_sublist _ h).map _).Subperm
#align multiset.powerset_len_mono Multiset.powersetLen_mono

/- warning: multiset.powerset_len_empty -> Multiset.powersetLen_empty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (n : Nat) {s : Multiset.{u1} α}, (LT.lt.{0} Nat Nat.hasLt (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) s) n) -> (Eq.{succ u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.powersetLen.{u1} α n s) (OfNat.ofNat.{u1} (Multiset.{u1} (Multiset.{u1} α)) 0 (OfNat.mk.{u1} (Multiset.{u1} (Multiset.{u1} α)) 0 (Zero.zero.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.hasZero.{u1} (Multiset.{u1} α))))))
but is expected to have type
  forall {α : Type.{u1}} (n : Nat) {s : Multiset.{u1} α}, (LT.lt.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) instLTNat (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) s) n) -> (Eq.{succ u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.powersetLen.{u1} α n s) (OfNat.ofNat.{u1} (Multiset.{u1} (Multiset.{u1} α)) 0 (Zero.toOfNat0.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instZeroMultiset.{u1} (Multiset.{u1} α)))))
Case conversion may be inaccurate. Consider using '#align multiset.powerset_len_empty Multiset.powersetLen_emptyₓ'. -/
@[simp]
theorem powersetLen_empty {α : Type _} (n : ℕ) {s : Multiset α} (h : s.card < n) :
    powersetLen n s = 0 :=
  card_eq_zero.mp (Nat.choose_eq_zero_of_lt h ▸ card_powersetLen _ _)
#align multiset.powerset_len_empty Multiset.powersetLen_empty

/- warning: multiset.powerset_len_card_add -> Multiset.powersetLen_card_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Multiset.{u1} α) {i : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) i) -> (Eq.{succ u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.powersetLen.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) s) i) s) (OfNat.ofNat.{u1} (Multiset.{u1} (Multiset.{u1} α)) 0 (OfNat.mk.{u1} (Multiset.{u1} (Multiset.{u1} α)) 0 (Zero.zero.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.hasZero.{u1} (Multiset.{u1} α))))))
but is expected to have type
  forall {α : Type.{u1}} (s : Multiset.{u1} α) {i : Nat}, (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) i) -> (Eq.{succ u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.powersetLen.{u1} α (HAdd.hAdd.{0, 0, 0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) Nat ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) (instHAdd.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) s) instAddNat) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) s) i) s) (OfNat.ofNat.{u1} (Multiset.{u1} (Multiset.{u1} α)) 0 (Zero.toOfNat0.{u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instZeroMultiset.{u1} (Multiset.{u1} α)))))
Case conversion may be inaccurate. Consider using '#align multiset.powerset_len_card_add Multiset.powersetLen_card_addₓ'. -/
@[simp]
theorem powersetLen_card_add (s : Multiset α) {i : ℕ} (hi : 0 < i) :
    s.powersetLen (s.card + i) = 0 :=
  powersetLen_empty _ (lt_add_of_pos_right (card s) hi)
#align multiset.powerset_len_card_add Multiset.powersetLen_card_add

#print Multiset.powersetLen_map /-
theorem powersetLen_map {β : Type _} (f : α → β) (n : ℕ) (s : Multiset α) :
    powersetLen n (s.map f) = (powersetLen n s).map (map f) :=
  by
  induction' s using Multiset.induction with t s ih generalizing n
  · cases n <;> simp [powerset_len_zero_left, powerset_len_zero_right]
  · cases n <;> simp [ih, map_comp_cons]
#align multiset.powerset_len_map Multiset.powersetLen_map
-/

#print Multiset.pairwise_disjoint_powersetLen /-
theorem pairwise_disjoint_powersetLen (s : Multiset α) :
    Pairwise fun i j => Multiset.Disjoint (s.powersetLen i) (s.powersetLen j) :=
  fun i j h x hi hj =>
  h (Eq.trans (Multiset.mem_powersetLen.mp hi).right.symm (Multiset.mem_powersetLen.mp hj).right)
#align multiset.pairwise_disjoint_powerset_len Multiset.pairwise_disjoint_powersetLen
-/

/- warning: multiset.bind_powerset_len -> Multiset.bind_powerset_len is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (S : Multiset.{u1} α), Eq.{succ u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.bind.{0, u1} Nat (Multiset.{u1} α) (Multiset.range (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) S) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (k : Nat) => Multiset.powersetLen.{u1} α k S)) (Multiset.powerset.{u1} α S)
but is expected to have type
  forall {α : Type.{u1}} (S : Multiset.{u1} α), Eq.{succ u1} (Multiset.{u1} (Multiset.{u1} α)) (Multiset.bind.{0, u1} Nat (Multiset.{u1} α) (Multiset.range (HAdd.hAdd.{0, 0, 0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) S) Nat ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) S) (instHAdd.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) S) instAddNat) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) (fun (_x : Multiset.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddZeroClass.toAdd.{u1} (Multiset.{u1} α) (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.instOrderedCancelAddCommMonoidMultiset.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{u1} α) S) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (k : Nat) => Multiset.powersetLen.{u1} α k S)) (Multiset.powerset.{u1} α S)
Case conversion may be inaccurate. Consider using '#align multiset.bind_powerset_len Multiset.bind_powerset_lenₓ'. -/
theorem bind_powerset_len {α : Type _} (S : Multiset α) :
    (bind (Multiset.range (S.card + 1)) fun k => S.powersetLen k) = S.powerset :=
  by
  induction S using Quotient.inductionOn
  simp_rw [quot_mk_to_coe, powerset_coe', powerset_len_coe, ← coe_range, coe_bind, ← List.bind_map,
    coe_card]
  exact coe_eq_coe.mpr ((List.range_bind_sublistsLen_perm S).map _)
#align multiset.bind_powerset_len Multiset.bind_powerset_len

#print Multiset.nodup_powerset /-
@[simp]
theorem nodup_powerset {s : Multiset α} : Nodup (powerset s) ↔ Nodup s :=
  ⟨fun h => (nodup_of_le (map_single_le_powerset _) h).of_map _,
    Quotient.inductionOn s fun l h => by
      simp <;> refine' (nodup_sublists'.2 h).map_onₓ _ <;>
        exact fun x sx y sy e =>
          (h.sublist_ext (mem_sublists'.1 sx) (mem_sublists'.1 sy)).1 (Quotient.exact e)⟩
#align multiset.nodup_powerset Multiset.nodup_powerset
-/

alias nodup_powerset ↔ nodup.of_powerset nodup.powerset
#align multiset.nodup.of_powerset Multiset.Nodup.ofPowerset
#align multiset.nodup.powerset Multiset.Nodup.powerset

#print Multiset.Nodup.powersetLen /-
protected theorem Nodup.powersetLen {n : ℕ} {s : Multiset α} (h : Nodup s) :
    Nodup (powersetLen n s) :=
  nodup_of_le (powersetLen_le_powerset _ _) (nodup_powerset.2 h)
#align multiset.nodup.powerset_len Multiset.Nodup.powersetLen
-/

end Multiset


/-
Copyright (c) 2021 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Lean.Elab.Tactic.Basic
import Mathlib.Algebra.Ring.Basic
import Mathlib.Tactic.Core

namespace Lean

/--
  Return true if `e` is one of the following
  - A nat literal (numeral)
  - `Nat.zero`
  - `Nat.succ x` where `isNumeral x`
  - `OfNat.ofNat _ x _` where `isNumeral x` -/
partial def Expr.numeral? (e : Expr) : Option Nat :=
  if let some n := e.natLit? then n
  else
    let f := e.getAppFn
    if !f.isConst then none
    else
      let fName := f.constName!
      if fName == ``Nat.succ && e.getAppNumArgs == 1 then (numeral? e.appArg!).map Nat.succ
      else if fName == ``OfNat.ofNat && e.getAppNumArgs == 3 then numeral? (e.getArg! 1)
      else if fName == ``Nat.zero && e.getAppNumArgs == 0 then some 0
      else none

namespace Meta

namespace NormNum

def isNat [Semiring α] (a : α) (n : ℕ) := a = n

class LawfulOfNat (α) [Semiring α] (n) [OfNat α n] : Prop where
  isNat_ofNat : isNat (@OfNat.ofNat _ n ‹_› : α) n

instance (α) [Semiring α] [Nat.AtLeastTwo n] : LawfulOfNat α n := ⟨rfl⟩
instance (α) [Semiring α] : LawfulOfNat α (nat_lit 0) := ⟨Nat.cast_zero.symm⟩
instance (α) [Semiring α] : LawfulOfNat α (nat_lit 1) := ⟨Nat.cast_one.symm⟩
instance : LawfulOfNat Nat n := ⟨show n = Nat.cast n by simp⟩
instance : LawfulOfNat Int n := ⟨show Int.ofNat n = Nat.cast n by simp⟩

theorem isNat_rawNat (n : ℕ) : isNat n n := LawfulOfNat.isNat_ofNat

class LawfulZero (α) [Semiring α] [Zero α] : Prop where
  isNat_zero : isNat (Zero.zero : α) (nat_lit 0)

instance (α) [Semiring α] : LawfulZero α := ⟨Nat.cast_zero.symm⟩

class LawfulOne (α) [Semiring α] [One α] : Prop where
  isNat_one : isNat (One.one : α) (nat_lit 1)

instance (α) [Semiring α] : LawfulOne α := ⟨Nat.cast_one.symm⟩

theorem isNat_add {α} [Semiring α] : (a b : α) → (a' b' c : Nat) →
  isNat a a' → isNat b b' → Nat.add a' b' = c → isNat (a + b) c
| _, _, _, _, _, rfl, rfl, rfl => Nat.cast_add.symm

theorem isNat_mul {α} [Semiring α] : (a b : α) → (a' b' c : Nat) →
  isNat a a' → isNat b b' → Nat.mul a' b' = c → isNat (a * b) c
| _, _, _, _, _, rfl, rfl, rfl => Nat.cast_mul.symm

theorem isNat_pow {α} [Semiring α] : (a : α) → (b a' b' c : Nat) →
  isNat a a' → isNat b b' → Nat.pow a' b' = c → isNat (a ^ b) c
| _, _, _, _, _, rfl, rfl, rfl => by simp [isNat]

def instSemiringNat : Semiring Nat := inferInstance

partial def evalIsNat (u : Level) (α sα e : Expr) : MetaM (Expr × Expr) := do
  let (n, p) ← match e.getAppFnArgs with
  | (``HAdd.hAdd, #[_, _, _, _, a, b]) => evalBinOp ``NormNum.isNat_add (·+·) a b
  | (``HMul.hMul, #[_, _, _, _, a, b]) => evalBinOp ``NormNum.isNat_mul (·*·) a b
  | (``HPow.hPow, #[_, _, _, _, a, b]) => evalPow ``NormNum.isNat_pow (·^·) a b
  | (``OfNat.ofNat, #[_, ln, inst]) =>
    let some n := ln.natLit? | throwError "fail"
    let lawful ← synthInstance (mkApp4 (mkConst ``LawfulOfNat [u]) α sα ln inst)
    pure (ln, mkApp5 (mkConst ``LawfulOfNat.isNat_ofNat [u]) α sα ln inst lawful)
  | (``Zero.zero, #[_, inst]) =>
    let lawful ← synthInstance (mkApp3 (mkConst ``LawfulZero [u]) α sα inst)
    pure (mkNatLit 0, mkApp4 (mkConst ``LawfulZero.isNat_zero [u]) α sα inst lawful)
  | (``One.one, #[_, inst]) =>
    let lawful ← synthInstance (mkApp3 (mkConst ``LawfulOne [u]) α sα inst)
    pure (mkNatLit 1, mkApp4 (mkConst ``LawfulOne.isNat_one [u]) α sα inst lawful)
  | _ =>
    unless e.isNatLit do throwError "fail"
    pure (e, mkApp (mkConst ``isNat_rawNat) e)
  pure (n, mkApp2 (mkConst ``id [levelZero]) (mkApp4 (mkConst ``isNat [u]) α sα e n) p)
where
  evalBinOp (name : Name) (f : Nat → Nat → Nat) (a b : Expr) : MetaM (Expr × Expr) := do
    let (la, pa) ← evalIsNat u α sα a
    let (lb, pb) ← evalIsNat u α sα b
    let a' := la.natLit!
    let b' := lb.natLit!
    let c' := f a' b'
    let lc := mkRawNatLit c'
    pure (lc, mkApp10 (mkConst name [u]) α sα a b la lb lc pa pb (← mkEqRefl lc))
  evalPow (name : Name) (f : Nat → Nat → Nat) (a b : Expr) : MetaM (Expr × Expr) := do
    let (la, pa) ← evalIsNat u α sα a
    let (lb, pb) ← evalIsNat levelZero (mkConst ``Nat) (mkConst ``instSemiringNat) b
    let a' := la.natLit!
    let b' := lb.natLit!
    let c' := f a' b'
    let lc := mkRawNatLit c'
    pure (lc, mkApp10 (mkConst name [u]) α sα a b la lb lc pa pb (← mkEqRefl lc))

theorem eval_of_isNat {α} [Semiring α] (n) [OfNat α n] [LawfulOfNat α n] :
  (a : α) → isNat a n → a = OfNat.ofNat n
| _, rfl => LawfulOfNat.isNat_ofNat.symm

def eval (e : Expr) : MetaM (Expr × Expr) := do
  let α ← inferType e
  let Level.succ u _ ← getLevel α | throwError "fail"
  let sα ← synthInstance (mkApp (mkConst ``Semiring [u]) α)
  let (ln, p) ← evalIsNat u α sα e
  let ofNatInst ← synthInstance (mkApp2 (mkConst ``OfNat [u]) α ln)
  let lawfulInst ← synthInstance (mkApp4 (mkConst ``LawfulOfNat [u]) α sα ln ofNatInst)
  pure (mkApp3 (mkConst ``OfNat.ofNat [u]) α ln ofNatInst,
    mkApp7 (mkConst ``eval_of_isNat [u]) α sα ln ofNatInst lawfulInst e p)

theorem eval_eq_of_isNat {α} [Semiring α] :
  (a b : α) → (n : ℕ) → isNat a n → isNat b n → a = b
| _, _, _, rfl, rfl => rfl

def evalEq (α a b : Expr) : MetaM Expr := do
  let Level.succ u _ ← getLevel α | throwError "fail"
  let sα ← synthInstance (mkApp (mkConst ``Semiring [u]) α)
  let (ln, pa) ← evalIsNat u α sα a
  let (ln', pb) ← evalIsNat u α sα b
  guard (ln.natLit! == ln'.natLit!)
  pure $ mkApp7 (mkConst ``eval_eq_of_isNat [u]) α sα a b ln pa pb

end NormNum
end Meta

namespace Tactic

open Lean.Parser.Tactic in
syntax (name := normNum) "norm_num" (" [" simpArg,* "]")? (ppSpace location)? : tactic

open Meta Elab.Tactic in
elab_rules : tactic | `(tactic| norm_num) => do
  liftMetaTactic fun g => do
    let some (α, lhs, rhs) ← matchEq? (← getMVarType g) | throwError "fail"
    let p ← NormNum.evalEq α lhs rhs
    assignExprMVar g p
    pure []

end Tactic

end Lean

variable (α) [Semiring α]
example : (1 + 0 : α) = (0 + 1 : α) := by norm_num
example : (0 + (2 + 3) + 1 : α) = 6 := by norm_num
example : (70 * (33 + 2) : α) = 2450 := by norm_num
example : (8 + 2 ^ 2 * 3 : α) = 20 := by norm_num
example : ((2 * 1 + 1) ^ 2 : α) = (3 * 3 : α) := by norm_num