l3lab/ntp-mathlib huggingface dataset:
@[to_additive exists_FS_of_large]
theorem exists_FP_of_large {M} [Semigroup M] (U : Ultrafilter M) (U_idem : U * U = U) (s₀ : Set M)
    (sU : s₀ ∈ U) : ∃ a, FP a ⊆ s₀ := by
  /- Informally: given a `U`-large set `s₀`, the set `s₀ ∩ { m | ∀ᶠ m' in U, m * m' ∈ s₀ }` is also
  `U`-large (since `U` is idempotent). Thus in particular there is an `a₀` in this intersection. Now
  let `s₁` be the intersection `s₀ ∩ { m | a₀ * m ∈ s₀ }`. By choice of `a₀`, this is again
  `U`-large, so we can repeat the argument starting from `s₁`, obtaining `a₁`, `s₂`, etc.
  This gives the desired infinite sequence. -/
  have exists_elem : ∀ {s : Set M} (_hs : s ∈ U), (s ∩ { m | ∀ᶠ m' in U, m * m' ∈ s }).Nonempty :=
    fun {s} hs => Ultrafilter.nonempty_of_mem (inter_mem hs <| by rwa [← U_idem] at hs)
  let elem : { s // s ∈ U } → M := fun p => (exists_elem p.property).some
  let succ : {s // s ∈ U} → {s // s ∈ U} := fun (p : {s // s ∈ U}) =>
        ⟨p.val ∩ {m : M | elem p * m ∈ p.val},
         inter_mem p.property
           (show (exists_elem p.property).some ∈ {m : M | ∀ᶠ (m' : M) in ↑U, m * m' ∈ p.val} from
              p.val.inter_subset_right {m : M | ∀ᶠ (m' : M) in ↑U, m * m' ∈ p.val}
                (exists_elem p.property).some_mem)⟩
  use Stream'.corec elem succ (Subtype.mk s₀ sU)
  suffices ∀ (a : Stream' M), ∀ m ∈ FP a, ∀ p, a = Stream'.corec elem succ p → m ∈ p.val by
    intro m hm
    exact this _ m hm ⟨s₀, sU⟩ rfl
  clear sU s₀
  intro a m h
  induction' h with b b n h ih b n h ih
  · rintro p rfl
    rw [Stream'.corec_eq, Stream'.head_cons]
    exact Set.inter_subset_left _ _ (Set.Nonempty.some_mem _)
  · rintro p rfl
    refine' Set.inter_subset_left _ _ (ih (succ p) _)
    rw [Stream'.corec_eq, Stream'.tail_cons]
  · rintro p rfl
    

Proof state:
case h.cons
M : Type u_1
inst✝ : Semigroup M
U : Ultrafilter M
U_idem : U * U = U
exists_elem : ∀ {s : Set M}, s ∈ U → Set.Nonempty (s ∩ {m | ∀ᶠ (m' : M) in ↑U, m * m' ∈ s})
elem : { s // s ∈ U } → M := fun p => Set.Nonempty.some (_ : Set.Nonempty (↑p ∩ {m | ∀ᶠ (m' : M) in ↑U, m * m' ∈ ↑p}))
succ : { s // s ∈ U } → { s // s ∈ U } :=
  fun p => { val := ↑p ∩ {m | elem p * m ∈ ↑p}, property := (_ : ↑p ∩ {m | elem p * m ∈ ↑p} ∈ ↑U) }
a : Stream' M
m n : M
p : { s // s ∈ U }
h : FP (Stream'.tail (Stream'.corec elem succ p)) n
ih : ∀ (p_1 : { s // s ∈ U }), Stream'.tail (Stream'.corec elem succ p) = Stream'.corec elem succ p_1 → n ∈ ↑p_1
⊢ Stream'.head (Stream'.corec elem succ p) * n ∈ ↑p

Next tactic:
have := Set.inter_subset_right _ _ (ih (succ p) ?_)
@[to_additive exists_FS_of_large]
theorem exists_FP_of_large {M} [Semigroup M] (U : Ultrafilter M) (U_idem : U * U = U) (s₀ : Set M)
    (sU : s₀ ∈ U) : ∃ a, FP a ⊆ s₀ := by
  /- Informally: given a `U`-large set `s₀`, the set `s₀ ∩ { m | ∀ᶠ m' in U, m * m' ∈ s₀ }` is also
  `U`-large (since `U` is idempotent). Thus in particular there is an `a₀` in this intersection. Now
  let `s₁` be the intersection `s₀ ∩ { m | a₀ * m ∈ s₀ }`. By choice of `a₀`, this is again
  `U`-large, so we can repeat the argument starting from `s₁`, obtaining `a₁`, `s₂`, etc.
  This gives the desired infinite sequence. -/
  have exists_elem : ∀ {s : Set M} (_hs : s ∈ U), (s ∩ { m | ∀ᶠ m' in U, m * m' ∈ s }).Nonempty :=
    fun {s} hs => Ultrafilter.nonempty_of_mem (inter_mem hs <| by rwa [← U_idem] at hs)
  let elem : { s // s ∈ U } → M := fun p => (exists_elem p.property).some
  let succ : {s // s ∈ U} → {s // s ∈ U} := fun (p : {s // s ∈ U}) =>
        ⟨p.val ∩ {m : M | elem p * m ∈ p.val},
         inter_mem p.property
           (show (exists_elem p.property).some ∈ {m : M | ∀ᶠ (m' : M) in ↑U, m * m' ∈ p.val} from
              p.val.inter_subset_right {m : M | ∀ᶠ (m' : M) in ↑U, m * m' ∈ p.val}
                (exists_elem p.property).some_mem)⟩
  use Stream'.corec elem succ (Subtype.mk s₀ sU)
  suffices ∀ (a : Stream' M), ∀ m ∈ FP a, ∀ p, a = Stream'.corec elem succ p → m ∈ p.val by
    intro m hm
    exact this _ m hm ⟨s₀, sU⟩ rfl
  clear sU s₀
  intro a m h
  induction' h with b b n h ih b n h ih
  · rintro p rfl
    rw [Stream'.corec_eq, Stream'.head_cons]
    exact Set.inter_subset_left _ _ (Set.Nonempty.some_mem _)
  · rintro p rfl
    refine' Set.inter_subset_left _ _ (ih (succ p) _)
    rw [Stream'.corec_eq, Stream'.tail_cons]
  · rintro p rfl
    have := Set.inter_subset_right _ _ (ih (succ p) ?_)
    · 

Proof state:
case h.cons.refine_2
M : Type u_1
inst✝ : Semigroup M
U : Ultrafilter M
U_idem : U * U = U
exists_elem : ∀ {s : Set M}, s ∈ U → Set.Nonempty (s ∩ {m | ∀ᶠ (m' : M) in ↑U, m * m' ∈ s})
elem : { s // s ∈ U } → M := fun p => Set.Nonempty.some (_ : Set.Nonempty (↑p ∩ {m | ∀ᶠ (m' : M) in ↑U, m * m' ∈ ↑p}))
succ : { s // s ∈ U } → { s // s ∈ U } :=
  fun p => { val := ↑p ∩ {m | elem p * m ∈ ↑p}, property := (_ : ↑p ∩ {m | elem p * m ∈ ↑p} ∈ ↑U) }
a : Stream' M
m n : M
p : { s // s ∈ U }
h : FP (Stream'.tail (Stream'.corec elem succ p)) n
ih : ∀ (p_1 : { s // s ∈ U }), Stream'.tail (Stream'.corec elem succ p) = Stream'.corec elem succ p_1 → n ∈ ↑p_1
this : n ∈ {m | elem p * m ∈ ↑p}
⊢ Stream'.head (Stream'.corec elem succ p) * n ∈ ↑p

Next tactic:
simpa only using this
@[to_additive exists_FS_of_large]
theorem exists_FP_of_large {M} [Semigroup M] (U : Ultrafilter M) (U_idem : U * U = U) (s₀ : Set M)
    (sU : s₀ ∈ U) : ∃ a, FP a ⊆ s₀ := by
  /- Informally: given a `U`-large set `s₀`, the set `s₀ ∩ { m | ∀ᶠ m' in U, m * m' ∈ s₀ }` is also
  `U`-large (since `U` is idempotent). Thus in particular there is an `a₀` in this intersection. Now
  let `s₁` be the intersection `s₀ ∩ { m | a₀ * m ∈ s₀ }`. By choice of `a₀`, this is again
  `U`-large, so we can repeat the argument starting from `s₁`, obtaining `a₁`, `s₂`, etc.
  This gives the desired infinite sequence. -/
  have exists_elem : ∀ {s : Set M} (_hs : s ∈ U), (s ∩ { m | ∀ᶠ m' in U, m * m' ∈ s }).Nonempty :=
    fun {s} hs => Ultrafilter.nonempty_of_mem (inter_mem hs <| by rwa [← U_idem] at hs)
  let elem : { s // s ∈ U } → M := fun p => (exists_elem p.property).some
  let succ : {s // s ∈ U} → {s // s ∈ U} := fun (p : {s // s ∈ U}) =>
        ⟨p.val ∩ {m : M | elem p * m ∈ p.val},
         inter_mem p.property
           (show (exists_elem p.property).some ∈ {m : M | ∀ᶠ (m' : M) in ↑U, m * m' ∈ p.val} from
              p.val.inter_subset_right {m : M | ∀ᶠ (m' : M) in ↑U, m * m' ∈ p.val}
                (exists_elem p.property).some_mem)⟩
  use Stream'.corec elem succ (Subtype.mk s₀ sU)
  suffices ∀ (a : Stream' M), ∀ m ∈ FP a, ∀ p, a = Stream'.corec elem succ p → m ∈ p.val by
    intro m hm
    exact this _ m hm ⟨s₀, sU⟩ rfl
  clear sU s₀
  intro a m h
  induction' h with b b n h ih b n h ih
  · rintro p rfl
    rw [Stream'.corec_eq, Stream'.head_cons]
    exact Set.inter_subset_left _ _ (Set.Nonempty.some_mem _)
  · rintro p rfl
    refine' Set.inter_subset_left _ _ (ih (succ p) _)
    rw [Stream'.corec_eq, Stream'.tail_cons]
  · rintro p rfl
    have := Set.inter_subset_right _ _ (ih (succ p) ?_)
    · simpa only using this
    

Proof state:
case h.cons.refine_1
M : Type u_1
inst✝ : Semigroup M
U : Ultrafilter M
U_idem : U * U = U
exists_elem : ∀ {s : Set M}, s ∈ U → Set.Nonempty (s ∩ {m | ∀ᶠ (m' : M) in ↑U, m * m' ∈ s})
elem : { s // s ∈ U } → M := fun p => Set.Nonempty.some (_ : Set.Nonempty (↑p ∩ {m | ∀ᶠ (m' : M) in ↑U, m * m' ∈ ↑p}))
succ : { s // s ∈ U } → { s // s ∈ U } :=
  fun p => { val := ↑p ∩ {m | elem p * m ∈ ↑p}, property := (_ : ↑p ∩ {m | elem p * m ∈ ↑p} ∈ ↑U) }
a : Stream' M
m n : M
p : { s // s ∈ U }
h : FP (Stream'.tail (Stream'.corec elem succ p)) n
ih : ∀ (p_1 : { s // s ∈ U }), Stream'.tail (Stream'.corec elem succ p) = Stream'.corec elem succ p_1 → n ∈ ↑p_1
⊢ Stream'.tail (Stream'.corec elem succ p) = Stream'.corec elem succ (succ p)

Next tactic:
rw [Stream'.corec_eq, Stream'.tail_cons]
@[to_additive FS_iter_tail_sub_FS]
theorem FP_drop_subset_FP {M} [Semigroup M] (a : Stream' M) (n : ℕ) : FP (a.drop n) ⊆ FP a := by
  

Proof state:
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
n : ℕ
⊢ FP (Stream'.drop n a) ⊆ FP a

Next tactic:
induction' n with n ih
@[to_additive FS_iter_tail_sub_FS]
theorem FP_drop_subset_FP {M} [Semigroup M] (a : Stream' M) (n : ℕ) : FP (a.drop n) ⊆ FP a := by
  induction' n with n ih
  · 

Proof state:
case zero
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
⊢ FP (Stream'.drop Nat.zero a) ⊆ FP a

Next tactic:
rfl
@[to_additive FS_iter_tail_sub_FS]
theorem FP_drop_subset_FP {M} [Semigroup M] (a : Stream' M) (n : ℕ) : FP (a.drop n) ⊆ FP a := by
  induction' n with n ih
  · rfl
  

Proof state:
case succ
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
n : ℕ
ih : FP (Stream'.drop n a) ⊆ FP a
⊢ FP (Stream'.drop (Nat.succ n) a) ⊆ FP a

Next tactic:
rw [Nat.succ_eq_one_add, ← Stream'.drop_drop]
@[to_additive FS_iter_tail_sub_FS]
theorem FP_drop_subset_FP {M} [Semigroup M] (a : Stream' M) (n : ℕ) : FP (a.drop n) ⊆ FP a := by
  induction' n with n ih
  · rfl
  rw [Nat.succ_eq_one_add, ← Stream'.drop_drop]
  

Proof state:
case succ
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
n : ℕ
ih : FP (Stream'.drop n a) ⊆ FP a
⊢ FP (Stream'.drop 1 (Stream'.drop n a)) ⊆ FP a

Next tactic:
exact _root_.trans (FP.tail _) ih
@[to_additive]
theorem FP.singleton {M} [Semigroup M] (a : Stream' M) (i : ℕ) : a.get i ∈ FP a := by
  

Proof state:
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i : ℕ
⊢ Stream'.get a i ∈ FP a

Next tactic:
induction' i with i ih generalizing a
@[to_additive]
theorem FP.singleton {M} [Semigroup M] (a : Stream' M) (i : ℕ) : a.get i ∈ FP a := by
  induction' i with i ih generalizing a
  · 

Proof state:
case zero
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
⊢ Stream'.get a Nat.zero ∈ FP a

Next tactic:
apply FP.head
@[to_additive]
theorem FP.singleton {M} [Semigroup M] (a : Stream' M) (i : ℕ) : a.get i ∈ FP a := by
  induction' i with i ih generalizing a
  · apply FP.head
  · 

Proof state:
case succ
M : Type u_1
inst✝ : Semigroup M
i : ℕ
ih : ∀ (a : Stream' M), Stream'.get a i ∈ FP a
a : Stream' M
⊢ Stream'.get a (Nat.succ i) ∈ FP a

Next tactic:
apply FP.tail
@[to_additive]
theorem FP.singleton {M} [Semigroup M] (a : Stream' M) (i : ℕ) : a.get i ∈ FP a := by
  induction' i with i ih generalizing a
  · apply FP.head
  · apply FP.tail
    

Proof state:
case succ.h
M : Type u_1
inst✝ : Semigroup M
i : ℕ
ih : ∀ (a : Stream' M), Stream'.get a i ∈ FP a
a : Stream' M
⊢ FP (Stream'.tail a) (Stream'.get a (Nat.succ i))

Next tactic:
apply ih
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  

Proof state:
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
⊢ Stream'.get a i * Stream'.get a j ∈ FP a

Next tactic:
refine' FP_drop_subset_FP _ i _
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  

Proof state:
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
⊢ Stream'.get a i * Stream'.get a j ∈ FP (Stream'.drop i a)

Next tactic:
rw [← Stream'.head_drop]
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  

Proof state:
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
⊢ Stream'.head (Stream'.drop i a) * Stream'.get a j ∈ FP (Stream'.drop i a)

Next tactic:
apply FP.cons
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  apply FP.cons
  

Proof state:
case h
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
⊢ FP (Stream'.tail (Stream'.drop i a)) (Stream'.get a j)

Next tactic:
rcases le_iff_exists_add.mp (Nat.succ_le_of_lt ij) with ⟨d, hd⟩
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  apply FP.cons
  rcases le_iff_exists_add.mp (Nat.succ_le_of_lt ij) with ⟨d, hd⟩
  -- Porting note: need to fix breakage of Set notation
  

Proof state:
case h.intro
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
d : ℕ
hd : j = Nat.succ i + d
⊢ FP (Stream'.tail (Stream'.drop i a)) (Stream'.get a j)

Next tactic:
change _ ∈ FP _
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  apply FP.cons
  rcases le_iff_exists_add.mp (Nat.succ_le_of_lt ij) with ⟨d, hd⟩
  -- Porting note: need to fix breakage of Set notation
  change _ ∈ FP _
  

Proof state:
case h.intro
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
d : ℕ
hd : j = Nat.succ i + d
⊢ Stream'.get a j ∈ FP (Stream'.tail (Stream'.drop i a))

Next tactic:
have := FP.singleton (a.drop i).tail d
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  apply FP.cons
  rcases le_iff_exists_add.mp (Nat.succ_le_of_lt ij) with ⟨d, hd⟩
  -- Porting note: need to fix breakage of Set notation
  change _ ∈ FP _
  have := FP.singleton (a.drop i).tail d
  

Proof state:
case h.intro
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
d : ℕ
hd : j = Nat.succ i + d
this : Stream'.get (Stream'.tail (Stream'.drop i a)) d ∈ FP (Stream'.tail (Stream'.drop i a))
⊢ Stream'.get a j ∈ FP (Stream'.tail (Stream'.drop i a))

Next tactic:
rw [Stream'.tail_eq_drop, Stream'.get_drop, Stream'.get_drop] at this
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  apply FP.cons
  rcases le_iff_exists_add.mp (Nat.succ_le_of_lt ij) with ⟨d, hd⟩
  -- Porting note: need to fix breakage of Set notation
  change _ ∈ FP _
  have := FP.singleton (a.drop i).tail d
  rw [Stream'.tail_eq_drop, Stream'.get_drop, Stream'.get_drop] at this
  

Proof state:
case h.intro
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
d : ℕ
hd : j = Nat.succ i + d
this : Stream'.get a (d + 1 + i) ∈ FP (Stream'.drop 1 (Stream'.drop i a))
⊢ Stream'.get a j ∈ FP (Stream'.tail (Stream'.drop i a))

Next tactic:
convert this
@[to_additive]
theorem FP.mul_two {M} [Semigroup M] (a : Stream' M) (i j : ℕ) (ij : i < j) :
    a.get i * a.get j ∈ FP a := by
  refine' FP_drop_subset_FP _ i _
  rw [← Stream'.head_drop]
  apply FP.cons
  rcases le_iff_exists_add.mp (Nat.succ_le_of_lt ij) with ⟨d, hd⟩
  -- Porting note: need to fix breakage of Set notation
  change _ ∈ FP _
  have := FP.singleton (a.drop i).tail d
  rw [Stream'.tail_eq_drop, Stream'.get_drop, Stream'.get_drop] at this
  convert this
  

Proof state:
case h.e'_4.h.e'_3
M : Type u_1
inst✝ : Semigroup M
a : Stream' M
i j : ℕ
ij : i < j
d : ℕ
hd : j = Nat.succ i + d
this : Stream'.get a (d + 1 + i) ∈ FP (Stream'.drop 1 (Stream'.drop i a))
⊢ j = d + 1 + i

Next tactic:
rw [hd, add_comm, Nat.succ_add, Nat.add_succ]
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  

Proof state:
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
hs : Finset.Nonempty s
⊢ (Finset.prod s fun i => Stream'.get a i) ∈ FP a

Next tactic:
refine' FP_drop_subset_FP _ (s.min' hs) _
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  

Proof state:
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
hs : Finset.Nonempty s
⊢ (Finset.prod s fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' s hs) a)

Next tactic:
induction' s using Finset.strongInduction with s ih
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  

Proof state:
case H
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
⊢ (Finset.prod s fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' s hs) a)

Next tactic:
rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  

Proof state:
case H
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
⊢ (Stream'.head (Stream'.drop (Finset.min' s hs) a) *
      Finset.prod (Finset.erase s (Finset.min' s hs)) fun x => Stream'.get a x) ∈
    FP (Stream'.drop (Finset.min' s hs) a)

Next tactic:
rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
  · 

Proof state:
case H.inl
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
h : Finset.erase s (Finset.min' s hs) = ∅
⊢ (Stream'.head (Stream'.drop (Finset.min' s hs) a) *
      Finset.prod (Finset.erase s (Finset.min' s hs)) fun x => Stream'.get a x) ∈
    FP (Stream'.drop (Finset.min' s hs) a)

Next tactic:
rw [h, Finset.prod_empty, mul_one]
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
  · rw [h, Finset.prod_empty, mul_one]
    

Proof state:
case H.inl
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
h : Finset.erase s (Finset.min' s hs) = ∅
⊢ Stream'.head (Stream'.drop (Finset.min' s hs) a) ∈ FP (Stream'.drop (Finset.min' s hs) a)

Next tactic:
exact FP.head _
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
  · rw [h, Finset.prod_empty, mul_one]
    exact FP.head _
  · 

Proof state:
case H.inr
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
h : Finset.Nonempty (Finset.erase s (Finset.min' s hs))
⊢ (Stream'.head (Stream'.drop (Finset.min' s hs) a) *
      Finset.prod (Finset.erase s (Finset.min' s hs)) fun x => Stream'.get a x) ∈
    FP (Stream'.drop (Finset.min' s hs) a)

Next tactic:
apply FP.cons
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
  · rw [h, Finset.prod_empty, mul_one]
    exact FP.head _
  · apply FP.cons
    

Proof state:
case H.inr.h
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
h : Finset.Nonempty (Finset.erase s (Finset.min' s hs))
⊢ FP (Stream'.tail (Stream'.drop (Finset.min' s hs) a))
    (Finset.prod (Finset.erase s (Finset.min' s hs)) fun x => Stream'.get a x)

Next tactic:
rw [Stream'.tail_eq_drop, Stream'.drop_drop, add_comm]
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
  · rw [h, Finset.prod_empty, mul_one]
    exact FP.head _
  · apply FP.cons
    rw [Stream'.tail_eq_drop, Stream'.drop_drop, add_comm]
    

Proof state:
case H.inr.h
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
h : Finset.Nonempty (Finset.erase s (Finset.min' s hs))
⊢ FP (Stream'.drop (Finset.min' s hs + 1) a) (Finset.prod (Finset.erase s (Finset.min' s hs)) fun x => Stream'.get a x)

Next tactic:
refine' Set.mem_of_subset_of_mem _ (ih _ (Finset.erase_ssubset <| s.min'_mem hs) h)
@[to_additive]
theorem FP.finset_prod {M} [CommMonoid M] (a : Stream' M) (s : Finset ℕ) (hs : s.Nonempty) :
    (s.prod fun i => a.get i) ∈ FP a := by
  refine' FP_drop_subset_FP _ (s.min' hs) _
  induction' s using Finset.strongInduction with s ih
  rw [← Finset.mul_prod_erase _ _ (s.min'_mem hs), ← Stream'.head_drop]
  rcases (s.erase (s.min' hs)).eq_empty_or_nonempty with h | h
  · rw [h, Finset.prod_empty, mul_one]
    exact FP.head _
  · apply FP.cons
    rw [Stream'.tail_eq_drop, Stream'.drop_drop, add_comm]
    refine' Set.mem_of_subset_of_mem _ (ih _ (Finset.erase_ssubset <| s.min'_mem hs) h)
    

Proof state:
case H.inr.h
M : Type u_1
inst✝ : CommMonoid M
a : Stream' M
s : Finset ℕ
ih :
  ∀ t ⊂ s, ∀ (hs : Finset.Nonempty t), (Finset.prod t fun i => Stream'.get a i) ∈ FP (Stream'.drop (Finset.min' t hs) a)
hs : Finset.Nonempty s
h : Finset.Nonempty (Finset.erase s (Finset.min' s hs))
⊢ FP (Stream'.drop (Finset.min' (Finset.erase s (Finset.min' s hs)) h) a) ⊆ FP (Stream'.drop (Finset.min' s hs + 1) a)

Next tactic:
have : s.min' hs + 1 ≤ (s.erase (s.min' hs)).min' h :=
      Nat.succ_le_of_lt (Finset.min'_lt_of_mem_erase_min' _ _ <| Finset.min'_mem _ _)