-
Notifications
You must be signed in to change notification settings - Fork 380
/
Copy pathLemmas.lean
226 lines (181 loc) · 8.57 KB
/
Lemmas.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Floris van Doorn, Sébastien Gouëzel, Alex J. Best
-/
import Mathlib.Algebra.BigOperators.Group.List.Basic
import Mathlib.Algebra.Divisibility.Basic
import Mathlib.Algebra.Group.Int.Units
import Mathlib.Data.List.Dedup
import Mathlib.Data.List.Flatten
import Mathlib.Data.List.Pairwise
import Mathlib.Data.List.Perm.Basic
import Mathlib.Data.List.Range
import Mathlib.Data.List.Rotate
import Mathlib.Data.List.ProdSigma
import Mathlib.Algebra.Group.Opposite
/-!
# Sums and products from lists
This file provides further results about `List.prod`, `List.sum`,
which calculate the product and sum of elements of a list
and `List.alternatingProd`, `List.alternatingSum`, their alternating counterparts.
-/
assert_not_imported Mathlib.Algebra.Order.Group.Nat
variable {ι α β M N P G : Type*}
namespace List
section Monoid
variable [Monoid M] [Monoid N] [Monoid P] {l l₁ l₂ : List M} {a : M}
@[to_additive]
theorem prod_isUnit : ∀ {L : List M}, (∀ m ∈ L, IsUnit m) → IsUnit L.prod
| [], _ => by simp
| h :: t, u => by
simp only [List.prod_cons]
exact IsUnit.mul (u h (mem_cons_self h t)) (prod_isUnit fun m mt => u m (mem_cons_of_mem h mt))
@[to_additive]
theorem prod_isUnit_iff {α : Type*} [CommMonoid α] {L : List α} :
IsUnit L.prod ↔ ∀ m ∈ L, IsUnit m := by
refine ⟨fun h => ?_, prod_isUnit⟩
induction L with
| nil => exact fun m' h' => False.elim (not_mem_nil m' h')
| cons m L ih =>
rw [prod_cons, IsUnit.mul_iff] at h
exact fun m' h' ↦ Or.elim (eq_or_mem_of_mem_cons h') (fun H => H.substr h.1) fun H => ih h.2 _ H
/-- If elements of a list commute with each other, then their product does not
depend on the order of elements. -/
@[to_additive "If elements of a list additively commute with each other, then their sum does not
depend on the order of elements."]
lemma Perm.prod_eq' (h : l₁ ~ l₂) (hc : l₁.Pairwise Commute) : l₁.prod = l₂.prod := by
refine h.foldr_eq' ?_ _
apply Pairwise.forall_of_forall
· intro x y h z
exact (h z).symm
· intros; rfl
· apply hc.imp
intro a b h z
rw [← mul_assoc, ← mul_assoc, h]
end Monoid
section Group
variable [Group G]
lemma prod_rotate_eq_one_of_prod_eq_one :
∀ {l : List G} (_ : l.prod = 1) (n : ℕ), (l.rotate n).prod = 1
| [], _, _ => by simp
| a :: l, hl, n => by
have : n % List.length (a :: l) ≤ List.length (a :: l) := le_of_lt (Nat.mod_lt _ (by simp))
rw [← List.take_append_drop (n % List.length (a :: l)) (a :: l)] at hl
rw [← rotate_mod, rotate_eq_drop_append_take this, List.prod_append, mul_eq_one_iff_inv_eq,
← one_mul (List.prod _)⁻¹, ← hl, List.prod_append, mul_assoc, mul_inv_cancel, mul_one]
end Group
variable [DecidableEq α]
/-- Summing the count of `x` over a list filtered by some `p` is just `countP` applied to `p` -/
theorem sum_map_count_dedup_filter_eq_countP (p : α → Bool) (l : List α) :
((l.dedup.filter p).map fun x => l.count x).sum = l.countP p := by
induction l with
| nil => simp
| cons a as h =>
simp_rw [List.countP_cons, List.count_cons, List.sum_map_add]
congr 1
· refine _root_.trans ?_ h
by_cases ha : a ∈ as
· simp [dedup_cons_of_mem ha]
· simp only [dedup_cons_of_not_mem ha, List.filter]
match p a with
| true => simp only [List.map_cons, List.sum_cons, List.count_eq_zero.2 ha, zero_add]
| false => simp only
· simp only [beq_iff_eq]
by_cases hp : p a
· refine _root_.trans (sum_map_eq_nsmul_single a _ fun _ h _ => by simp [h.symm]) ?_
simp [hp, count_dedup]
· refine _root_.trans (List.sum_eq_zero fun n hn => ?_) (by simp [hp])
obtain ⟨a', ha'⟩ := List.mem_map.1 hn
split_ifs at ha' with ha
· simp only [ha.symm, mem_filter, mem_dedup, find?, mem_cons, true_or, hp,
and_false, false_and, reduceCtorEq] at ha'
· exact ha'.2.symm
theorem sum_map_count_dedup_eq_length (l : List α) :
(l.dedup.map fun x => l.count x).sum = l.length := by
simpa using sum_map_count_dedup_filter_eq_countP (fun _ => True) l
end List
namespace List
lemma length_sigma {σ : α → Type*} (l₁ : List α) (l₂ : ∀ a, List (σ a)) :
length (l₁.sigma l₂) = (l₁.map fun a ↦ length (l₂ a)).sum := by
induction' l₁ with x l₁ IH
· rfl
· simp only [sigma_cons, length_append, length_map, IH, map, sum_cons]
lemma ranges_flatten : ∀ (l : List ℕ), l.ranges.flatten = range l.sum
| [] => rfl
| a :: l => by simp [ranges, ← map_flatten, ranges_flatten, range_add]
/-- The members of `l.ranges` have no duplicate -/
theorem ranges_nodup {l s : List ℕ} (hs : s ∈ ranges l) : s.Nodup :=
(List.pairwise_flatten.mp <| by rw [ranges_flatten]; exact nodup_range _).1 s hs
@[deprecated (since := "2024-10-15")] alias ranges_join := ranges_flatten
/-- Any entry of any member of `l.ranges` is strictly smaller than `l.sum`. -/
lemma mem_mem_ranges_iff_lt_sum (l : List ℕ) {n : ℕ} :
(∃ s ∈ l.ranges, n ∈ s) ↔ n < l.sum := by
rw [← mem_range, ← ranges_flatten, mem_flatten]
/-- In a flatten of sublists, taking the slice between the indices `A` and `B - 1` gives back the
original sublist of index `i` if `A` is the sum of the lengths of sublists of index `< i`, and
`B` is the sum of the lengths of sublists of index `≤ i`. -/
lemma drop_take_succ_flatten_eq_getElem (L : List (List α)) (i : Nat) (h : i < L.length) :
(L.flatten.take ((L.map length).take (i + 1)).sum).drop ((L.map length).take i).sum = L[i] := by
have : (L.map length).take i = ((L.take (i + 1)).map length).take i := by
simp [map_take, take_take, Nat.min_eq_left]
simp only [this, length_map, take_sum_flatten, drop_sum_flatten,
drop_take_succ_eq_cons_getElem, h, flatten, append_nil]
end List
namespace List
/-- If a product of integers is `-1`, then at least one factor must be `-1`. -/
theorem neg_one_mem_of_prod_eq_neg_one {l : List ℤ} (h : l.prod = -1) : (-1 : ℤ) ∈ l := by
obtain ⟨x, h₁, h₂⟩ := exists_mem_ne_one_of_prod_ne_one (ne_of_eq_of_ne h (by decide))
exact Or.resolve_left
(Int.isUnit_iff.mp (prod_isUnit_iff.mp
(h.symm ▸ ⟨⟨-1, -1, by decide, by decide⟩, rfl⟩ : IsUnit l.prod) x h₁)) h₂ ▸ h₁
theorem dvd_prod [CommMonoid M] {a} {l : List M} (ha : a ∈ l) : a ∣ l.prod := by
let ⟨s, t, h⟩ := append_of_mem ha
rw [h, prod_append, prod_cons, mul_left_comm]
exact dvd_mul_right _ _
theorem Sublist.prod_dvd_prod [CommMonoid M] {l₁ l₂ : List M} (h : l₁ <+ l₂) :
l₁.prod ∣ l₂.prod := by
obtain ⟨l, hl⟩ := h.exists_perm_append
rw [hl.prod_eq, prod_append]
exact dvd_mul_right _ _
section Alternating
variable [CommGroup α]
@[to_additive]
theorem alternatingProd_append :
∀ l₁ l₂ : List α,
alternatingProd (l₁ ++ l₂) = alternatingProd l₁ * alternatingProd l₂ ^ (-1 : ℤ) ^ length l₁
| [], l₂ => by simp
| a :: l₁, l₂ => by
simp_rw [cons_append, alternatingProd_cons, alternatingProd_append, length_cons, pow_succ',
Int.neg_mul, one_mul, zpow_neg, ← div_eq_mul_inv, div_div]
@[to_additive]
theorem alternatingProd_reverse :
∀ l : List α, alternatingProd (reverse l) = alternatingProd l ^ (-1 : ℤ) ^ (length l + 1)
| [] => by simp only [alternatingProd_nil, one_zpow, reverse_nil]
| a :: l => by
simp_rw [reverse_cons, alternatingProd_append, alternatingProd_reverse,
alternatingProd_singleton, alternatingProd_cons, length_reverse, length, pow_succ',
Int.neg_mul, one_mul, zpow_neg, inv_inv]
rw [mul_comm, ← div_eq_mul_inv, div_zpow]
end Alternating
end List
open List
namespace MulOpposite
variable [Monoid M]
lemma op_list_prod : ∀ l : List M, op l.prod = (l.map op).reverse.prod := by
intro l; induction l with
| nil => rfl
| cons x xs ih =>
rw [List.prod_cons, List.map_cons, List.reverse_cons', List.prod_concat, op_mul, ih]
lemma unop_list_prod (l : List Mᵐᵒᵖ) : l.prod.unop = (l.map unop).reverse.prod := by
rw [← op_inj, op_unop, MulOpposite.op_list_prod, map_reverse, map_map, reverse_reverse,
op_comp_unop, map_id]
end MulOpposite
section MonoidHom
variable [Monoid M] [Monoid N]
/-- A morphism into the opposite monoid acts on the product by acting on the reversed elements. -/
lemma unop_map_list_prod {F : Type*} [FunLike F M Nᵐᵒᵖ] [MonoidHomClass F M Nᵐᵒᵖ]
(f : F) (l : List M) :
(f l.prod).unop = (l.map (MulOpposite.unop ∘ f)).reverse.prod := by
rw [map_list_prod f l, MulOpposite.unop_list_prod, List.map_map]
end MonoidHom