forked from leanprover-community/mathlib4
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathBasic.lean
More file actions
521 lines (421 loc) · 20.5 KB
/
Copy pathBasic.lean
File metadata and controls
521 lines (421 loc) · 20.5 KB
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/-
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
-/
module
public import Mathlib.Algebra.Divisibility.Basic
public import Mathlib.Algebra.Group.Hom.Defs
public import Mathlib.Algebra.BigOperators.Group.List.Defs
public import Mathlib.Data.List.TakeDrop
public import Mathlib.Data.List.Forall2
public import Mathlib.Data.List.Perm.Basic
public import Mathlib.Algebra.Group.Basic
public import Mathlib.Algebra.Group.Commute.Defs
public import Mathlib.Algebra.Group.Nat.Defs
public import Mathlib.Algebra.Group.Int.Defs
public import Mathlib.Order.Basic
/-!
# Sums and products from lists
This file provides basic 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.
-/
public section
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}
open scoped Relator in
@[to_additive]
theorem rel_prod {R : M → N → Prop} (h : R 1 1) (hf : (R ⇒ R ⇒ R) (· * ·) (· * ·)) :
(Forall₂ R ⇒ R) prod prod :=
rel_foldr hf h
@[to_additive]
theorem prod_hom_nonempty {l : List M} {F : Type*} [FunLike F M N] [MulHomClass F M N] (f : F)
(hl : l ≠ []) : (l.map f).prod = f l.prod :=
match l, hl with | x :: xs, hl => by induction xs generalizing x <;> simp_all
@[to_additive]
theorem prod_hom (l : List M) {F : Type*} [FunLike F M N] [MonoidHomClass F M N] (f : F) :
(l.map f).prod = f l.prod := by
simp only [prod, foldr_map, ← map_one f]
exact l.foldr_hom f (fun x y => (map_mul f x y).symm)
@[to_additive]
theorem prod_hom₂_nonempty {l : List ι} (f : M → N → P)
(hf : ∀ a b c d, f (a * b) (c * d) = f a c * f b d) (f₁ : ι → M) (f₂ : ι → N) (hl : l ≠ []) :
(l.map fun i => f (f₁ i) (f₂ i)).prod = f (l.map f₁).prod (l.map f₂).prod := by
match l, hl with | x :: xs, hl => induction xs generalizing x <;> simp_all
@[to_additive]
theorem prod_hom₂ (l : List ι) (f : M → N → P) (hf : ∀ a b c d, f (a * b) (c * d) = f a c * f b d)
(hf' : f 1 1 = 1) (f₁ : ι → M) (f₂ : ι → N) :
(l.map fun i => f (f₁ i) (f₂ i)).prod = f (l.map f₁).prod (l.map f₂).prod := by
simp only [prod_eq_foldr, foldr_map]
rw [← foldr_hom₂ l f _ _ ((fun x y => f (f₁ x) (f₂ x) * y)) _ _ (by simp [hf]), hf']
@[to_additive (attr := simp)]
theorem prod_map_mul {M : Type*} [CommMonoid M] {l : List ι} {f g : ι → M} :
(l.map fun i => f i * g i).prod = (l.map f).prod * (l.map g).prod :=
l.prod_hom₂ (· * ·) mul_mul_mul_comm (mul_one _) _ _
@[to_additive]
theorem prod_map_hom (L : List ι) (f : ι → M) {G : Type*} [FunLike G M N] [MonoidHomClass G M N]
(g : G) :
(L.map (g ∘ f)).prod = g (L.map f).prod := by rw [← prod_hom, map_map]
@[to_additive (attr := simp)]
theorem prod_take_mul_prod_drop (L : List M) (i : ℕ) :
(L.take i).prod * (L.drop i).prod = L.prod := by
simp [← prod_append]
@[to_additive (attr := simp)]
theorem prod_take_succ (L : List M) (i : ℕ) (p : i < L.length) :
(L.take (i + 1)).prod = (L.take i).prod * L[i] := by
rw [← take_concat_get' _ _ p, prod_append]
simp
/-- A list with product not one must have positive length. -/
@[to_additive /-- A list with sum not zero must have positive length. -/]
theorem length_pos_of_prod_ne_one (L : List M) (h : L.prod ≠ 1) : 0 < L.length := by
cases L
· simp at h
· simp
/-- A list with product greater than one must have positive length. -/
@[to_additive length_pos_of_sum_pos /-- A list with positive sum must have positive length. -/]
theorem length_pos_of_one_lt_prod [Preorder M] (L : List M) (h : 1 < L.prod) : 0 < L.length :=
length_pos_of_prod_ne_one L h.ne'
/-- A list with product less than one must have positive length. -/
@[to_additive /-- A list with negative sum must have positive length. -/]
theorem length_pos_of_prod_lt_one [Preorder M] (L : List M) (h : L.prod < 1) : 0 < L.length :=
length_pos_of_prod_ne_one L h.ne
@[to_additive]
theorem prod_set :
∀ (L : List M) (n : ℕ) (a : M),
(L.set n a).prod =
((L.take n).prod * if n < L.length then a else 1) * (L.drop (n + 1)).prod
| x :: xs, 0, a => by simp [set]
| x :: xs, i + 1, a => by simp [set, prod_set xs i a, mul_assoc]
| [], _, _ => by simp [set]
/-- We'd like to state this as `L.headI * L.tail.prod = L.prod`, but because `L.headI` relies on an
inhabited instance to return a garbage value on the empty list, this is not possible.
Instead, we write the statement in terms of `L[0]?.getD 1`.
-/
@[to_additive /-- We'd like to state this as `L.headI + L.tail.sum = L.sum`, but because `L.headI`
relies on an inhabited instance to return a garbage value on the empty list, this is not possible.
Instead, we write the statement in terms of `L[0]?.getD 0`. -/]
theorem getElem?_zero_mul_tail_prod (l : List M) : l[0]?.getD 1 * l.tail.prod = l.prod := by
cases l <;> simp
/-- Same as `get?_zero_mul_tail_prod`, but avoiding the `List.headI` garbage complication by
requiring the list to be nonempty. -/
@[to_additive /-- Same as `get?_zero_add_tail_sum`, but avoiding the `List.headI` garbage
complication by requiring the list to be nonempty. -/]
theorem headI_mul_tail_prod_of_ne_nil [Inhabited M] (l : List M) (h : l ≠ []) :
l.headI * l.tail.prod = l.prod := by cases l <;> [contradiction; simp]
@[to_additive]
theorem _root_.Commute.list_prod_right (l : List M) (y : M) (h : ∀ x ∈ l, Commute y x) :
Commute y l.prod := by
induction l with
| nil => simp
| cons z l IH =>
rw [List.forall_mem_cons] at h
rw [List.prod_cons]
exact Commute.mul_right h.1 (IH h.2)
@[to_additive]
theorem _root_.Commute.list_prod_left (l : List M) (y : M) (h : ∀ x ∈ l, Commute x y) :
Commute l.prod y :=
((Commute.list_prod_right _ _) fun _ hx => (h _ hx).symm).symm
@[to_additive] lemma prod_range_succ (f : ℕ → M) (n : ℕ) :
((range n.succ).map f).prod = ((range n).map f).prod * f n := by
rw [range_succ, map_append, map_singleton, prod_append, prod_cons, prod_nil, mul_one]
/-- A variant of `prod_range_succ` which pulls off the first term in the product rather than the
last. -/
@[to_additive /-- A variant of `sum_range_succ` which pulls off the first term in the sum rather
than the last. -/]
lemma prod_range_succ' (f : ℕ → M) (n : ℕ) :
((range n.succ).map f).prod = f 0 * ((range n).map fun i ↦ f i.succ).prod := by
rw [range_succ_eq_map]
simp [Function.comp_def]
@[to_additive] lemma prod_eq_one (hl : ∀ x ∈ l, x = 1) : l.prod = 1 := by
induction l with
| nil => rfl
| cons i l hil =>
rw [List.prod_cons, hil fun x hx ↦ hl _ (mem_cons_of_mem i hx),
hl _ mem_cons_self, one_mul]
@[to_additive] lemma exists_mem_ne_one_of_prod_ne_one (h : l.prod ≠ 1) :
∃ x ∈ l, x ≠ (1 : M) := by simpa only [not_forall, exists_prop] using mt prod_eq_one h
@[to_additive]
lemma prod_erase_of_comm [DecidableEq M] (ha : a ∈ l) (comm : ∀ x ∈ l, ∀ y ∈ l, x * y = y * x) :
a * (l.erase a).prod = l.prod := by
induction l with
| nil => simp only [not_mem_nil] at ha
| cons b l ih =>
obtain rfl | ⟨ne, h⟩ := List.eq_or_ne_mem_of_mem ha
· simp only [erase_cons_head, prod_cons]
rw [List.erase, beq_false_of_ne ne.symm, List.prod_cons, List.prod_cons, ← mul_assoc,
comm a ha b mem_cons_self, mul_assoc,
ih h fun x hx y hy ↦ comm _ (List.mem_cons_of_mem b hx) _ (List.mem_cons_of_mem b hy)]
@[to_additive]
lemma prod_map_eq_pow_single [DecidableEq α] {l : List α} (a : α) (f : α → M)
(hf : ∀ a', a' ≠ a → a' ∈ l → f a' = 1) : (l.map f).prod = f a ^ l.count a := by
induction l generalizing a with
| nil => rw [map_nil, prod_nil, count_nil, _root_.pow_zero]
| cons a' as h =>
specialize h a fun a' ha' hfa' => hf a' ha' (mem_cons_of_mem _ hfa')
rw [List.map_cons, List.prod_cons, count_cons, h]
simp only [beq_iff_eq]
split_ifs with ha'
· rw [ha', _root_.pow_succ']
· rw [hf a' ha' mem_cons_self, one_mul, add_zero]
@[to_additive]
lemma prod_eq_pow_single [DecidableEq M] (a : M) (h : ∀ a', a' ≠ a → a' ∈ l → a' = 1) :
l.prod = a ^ l.count a :=
_root_.trans (by rw [map_id]) (prod_map_eq_pow_single a id h)
@[to_additive (attr := simp)]
theorem prod_insertIdx {i} (hlen : i ≤ l.length) (hcomm : ∀ a' ∈ l.take i, Commute a a') :
(l.insertIdx i a).prod = a * l.prod := by
induction i generalizing l
case zero => rfl
case succ i ih =>
obtain ⟨hd, tl, rfl⟩ := exists_cons_of_length_pos (Nat.zero_lt_of_lt hlen)
simp only [insertIdx_succ_cons, prod_cons,
ih (Nat.le_of_lt_succ hlen) (fun a' a'_mem => hcomm a' (mem_of_mem_tail a'_mem))]
exact Commute.left_comm (hcomm hd (mem_of_mem_head? rfl)).symm tl.prod
@[to_additive (attr := simp)]
theorem mul_prod_eraseIdx {i} (hlen : i < l.length) (hcomm : ∀ a' ∈ l.take i, Commute l[i] a') :
l[i] * (l.eraseIdx i).prod = l.prod := by
rw [← prod_insertIdx (by grind : i ≤ (l.eraseIdx i).length) (fun a' a'_mem =>
hcomm a' (by rwa [take_eraseIdx_eq_take_of_le l i i (Nat.le_refl i)] at a'_mem)),
insertIdx_eraseIdx_getElem hlen]
end Monoid
section CommMonoid
variable [CommMonoid M] {a : M} {l l₁ l₂ : List M}
@[to_additive (attr := simp)]
theorem CommMonoid.prod_insertIdx {i} (h : i ≤ l.length) : (l.insertIdx i a).prod = a * l.prod :=
List.prod_insertIdx h (fun a' _ ↦ Commute.all a a')
@[to_additive (attr := simp)]
theorem CommMonoid.mul_prod_eraseIdx {i} (h : i < l.length) : l[i] * (l.eraseIdx i).prod = l.prod :=
List.mul_prod_eraseIdx h (fun a' _ ↦ Commute.all l[i] a')
@[to_additive (attr := simp)]
lemma prod_erase [DecidableEq M] (ha : a ∈ l) : a * (l.erase a).prod = l.prod :=
prod_erase_of_comm ha fun x _ y _ ↦ mul_comm x y
@[to_additive (attr := simp)]
lemma prod_map_erase [DecidableEq α] (f : α → M) {a} :
∀ {l : List α}, a ∈ l → f a * ((l.erase a).map f).prod = (l.map f).prod
| b :: l, h => by
obtain rfl | ⟨ne, h⟩ := List.eq_or_ne_mem_of_mem h
· simp only [map, erase_cons_head, prod_cons]
· simp only [map, erase_cons_tail (not_beq_of_ne ne.symm), prod_cons, prod_map_erase _ h,
mul_left_comm (f a) (f b)]
@[to_additive] lemma Perm.prod_eq (h : Perm l₁ l₂) : prod l₁ = prod l₂ := h.foldr_op_eq
set_option linter.existingAttributeWarning false in
attribute [to_additive existing] prod_reverse
@[to_additive]
lemma prod_mul_prod_eq_prod_zipWith_mul_prod_drop :
∀ l l' : List M,
l.prod * l'.prod =
(zipWith (· * ·) l l').prod * (l.drop l'.length).prod * (l'.drop l.length).prod
| [], ys => by simp
| xs, [] => by simp
| x :: xs, y :: ys => by
simp only [drop, zipWith_cons_cons, prod_cons]
conv =>
lhs; rw [mul_assoc]; right; rw [mul_comm, mul_assoc]; right
rw [mul_comm, prod_mul_prod_eq_prod_zipWith_mul_prod_drop xs ys]
simp [mul_assoc]
@[to_additive]
lemma prod_mul_prod_eq_prod_zipWith_of_length_eq (l l' : List M) (h : l.length = l'.length) :
l.prod * l'.prod = (zipWith (· * ·) l l').prod := by
apply (prod_mul_prod_eq_prod_zipWith_mul_prod_drop l l').trans
rw [← h, drop_length, h, drop_length, prod_nil, mul_one, mul_one]
@[to_additive]
lemma prod_map_ite (p : α → Prop) [DecidablePred p] (f g : α → M) (l : List α) :
(l.map fun a => if p a then f a else g a).prod =
((l.filter p).map f).prod * ((l.filter fun a ↦ ¬p a).map g).prod := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, filter_cons, prod_cons] at ih ⊢
rw [ih]
clear ih
by_cases hx : p x
· simp only [hx, ↓reduceIte, decide_not, decide_true, map_cons, prod_cons, not_true_eq_false,
decide_false, Bool.false_eq_true, mul_assoc]
· simp only [hx, ↓reduceIte, decide_not, decide_false, Bool.false_eq_true, not_false_eq_true,
decide_true, map_cons, prod_cons, mul_left_comm]
@[to_additive]
lemma prod_map_filter_mul_prod_map_filter_not (p : α → Prop) [DecidablePred p] (f : α → M)
(l : List α) :
((l.filter p).map f).prod * ((l.filter fun x => ¬p x).map f).prod = (l.map f).prod := by
rw [← prod_map_ite]
simp only [ite_self]
end CommMonoid
@[to_additive]
lemma eq_of_prod_take_eq [LeftCancelMonoid M] {L L' : List M} (h : L.length = L'.length)
(h' : ∀ i ≤ L.length, (L.take i).prod = (L'.take i).prod) : L = L' := by
refine ext_get h fun i h₁ h₂ => ?_
have : (L.take (i + 1)).prod = (L'.take (i + 1)).prod := h' _ (Nat.succ_le_of_lt h₁)
rw [prod_take_succ L i h₁, prod_take_succ L' i h₂, h' i (Nat.le_of_lt h₁)] at this
convert mul_left_cancel this
section Group
variable [Group G]
/-- This is the `List.prod` version of `mul_inv_rev` -/
@[to_additive /-- This is the `List.sum` version of `add_neg_rev` -/]
theorem prod_inv_reverse : ∀ L : List G, L.prod⁻¹ = (L.map fun x => x⁻¹).reverse.prod
| [] => by simp
| x :: xs => by simp [prod_append, prod_inv_reverse xs]
/-- A non-commutative variant of `List.prod_reverse` -/
@[to_additive /-- A non-commutative variant of `List.sum_reverse` -/]
theorem prod_reverse_noncomm : ∀ L : List G, L.reverse.prod = (L.map fun x => x⁻¹).prod⁻¹ := by
simp [prod_inv_reverse]
/-- Counterpart to `List.prod_take_succ` when we have an inverse operation -/
@[to_additive (attr := simp)
/-- Counterpart to `List.sum_take_succ` when we have a negation operation -/]
theorem prod_drop_succ :
∀ (L : List G) (i : ℕ) (p : i < L.length), (L.drop (i + 1)).prod = L[i]⁻¹ * (L.drop i).prod
| [], _, p => False.elim (Nat.not_lt_zero _ p)
| _ :: _, 0, _ => by simp
| _ :: xs, i + 1, p => prod_drop_succ xs i (Nat.lt_of_succ_lt_succ p)
/-- Cancellation of a telescoping product. -/
@[to_additive /-- Cancellation of a telescoping sum. -/]
theorem prod_range_div' (n : ℕ) (f : ℕ → G) :
((range n).map fun k ↦ f k / f (k + 1)).prod = f 0 / f n := by
induction n with
| zero => exact (div_self' (f 0)).symm
| succ n h => simp [range_succ, prod_append, map_append, h]
end Group
section CommGroup
variable [CommGroup G]
/-- This is the `List.prod` version of `mul_inv` -/
@[to_additive /-- This is the `List.sum` version of `add_neg` -/]
theorem prod_inv {K : Type*} [DivisionCommMonoid K] :
∀ L : List K, L.prod⁻¹ = (L.map fun x => x⁻¹).prod
| [] => by simp
| x :: xs => by simp [mul_comm, prod_inv xs]
/-- Cancellation of a telescoping product. -/
@[to_additive /-- Cancellation of a telescoping sum. -/]
theorem prod_range_div (n : ℕ) (f : ℕ → G) :
((range n).map fun k ↦ f (k + 1) / f k).prod = f n / f 0 := by
have h : ((·⁻¹) ∘ fun k ↦ f (k + 1) / f k) = fun k ↦ f k / f (k + 1) := by ext; apply inv_div
rw [← inv_inj, prod_inv, map_map, inv_div, h, prod_range_div']
/-- Alternative version of `List.prod_set` when the list is over a group -/
@[to_additive /-- Alternative version of `List.sum_set` when the list is over a group -/]
theorem prod_set' (L : List G) (n : ℕ) (a : G) :
(L.set n a).prod = L.prod * if hn : n < L.length then L[n]⁻¹ * a else 1 := by
refine (prod_set L n a).trans ?_
split_ifs with hn
· rw [mul_comm _ a, mul_assoc a, prod_drop_succ L n hn, mul_comm _ (drop n L).prod, ←
mul_assoc (take n L).prod, prod_take_mul_prod_drop, mul_comm a, mul_assoc]
· simp (disch := grind) [take_of_length_le, drop_eq_nil_of_le]
@[to_additive]
lemma prod_map_ite_eq {A : Type*} [DecidableEq A] (l : List A) (f g : A → G) (a : A) :
(l.map fun x => if x = a then f x else g x).prod
= (f a / g a) ^ (l.count a) * (l.map g).prod := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, prod_cons, count_cons] at ih ⊢
rw [ih]
clear ih
by_cases hx : x = a
· simp only [hx, ite_true, pow_add, pow_one, div_eq_mul_inv, mul_assoc, mul_comm, mul_left_comm,
mul_inv_cancel_left, beq_self_eq_true]
· simp only [hx, ite_false, add_zero, mul_assoc, mul_comm (g x) _, beq_iff_eq]
end CommGroup
theorem sum_const_nat (m n : ℕ) : sum (replicate m n) = m * n :=
sum_replicate m n
/-!
Several lemmas about sum/head/tail for `List ℕ`.
These are hard to generalize well, as they rely on the fact that `default ℕ = 0`.
If desired, we could add a class stating that `default = 0`.
-/
/-- This relies on `default ℕ = 0`. -/
theorem headI_add_tail_sum (L : List ℕ) : L.headI + L.tail.sum = L.sum := by
cases L <;> simp
/-- This relies on `default ℕ = 0`. -/
theorem headI_le_sum (L : List ℕ) : L.headI ≤ L.sum :=
Nat.le.intro (headI_add_tail_sum L)
/-- This relies on `default ℕ = 0`. -/
theorem tail_sum (L : List ℕ) : L.tail.sum = L.sum - L.headI := by
rw [← headI_add_tail_sum L, add_comm, Nat.add_sub_cancel_right]
section Alternating
section
variable [One G] [Mul G] [Inv G]
@[to_additive (attr := simp)]
theorem alternatingProd_nil : alternatingProd ([] : List G) = 1 :=
rfl
@[to_additive (attr := simp)]
theorem alternatingProd_singleton (a : G) : alternatingProd [a] = a :=
rfl
@[to_additive]
theorem alternatingProd_cons_cons' (a b : G) (l : List G) :
alternatingProd (a :: b :: l) = a * b⁻¹ * alternatingProd l :=
rfl
end
@[to_additive]
theorem alternatingProd_cons_cons [DivInvMonoid G] (a b : G) (l : List G) :
alternatingProd (a :: b :: l) = a / b * alternatingProd l := by
rw [div_eq_mul_inv, alternatingProd_cons_cons']
variable [CommGroup G]
@[to_additive]
theorem alternatingProd_cons' :
∀ (a : G) (l : List G), alternatingProd (a :: l) = a * (alternatingProd l)⁻¹
| a, [] => by rw [alternatingProd_nil, inv_one, mul_one, alternatingProd_singleton]
| a, b :: l => by
rw [alternatingProd_cons_cons', alternatingProd_cons' b l, mul_inv, inv_inv, mul_assoc]
@[to_additive (attr := simp)]
theorem alternatingProd_cons (a : G) (l : List G) :
alternatingProd (a :: l) = a / alternatingProd l := by
rw [div_eq_mul_inv, alternatingProd_cons']
end Alternating
lemma sum_nat_mod (l : List ℕ) (n : ℕ) : l.sum % n = (l.map (· % n)).sum % n := by
induction l with
| nil => simp only [map_nil]
| cons a l ih =>
simpa only [map_cons, sum_cons, Nat.mod_add_mod, Nat.add_mod_mod] using congr((a + $ih) % n)
lemma prod_nat_mod (l : List ℕ) (n : ℕ) : l.prod % n = (l.map (· % n)).prod % n := by
induction l with
| nil => simp only [map_nil]
| cons a l ih =>
simpa only [prod_cons, map_cons, Nat.mod_mul_mod, Nat.mul_mod_mod] using congr((a * $ih) % n)
lemma sum_int_mod (l : List ℤ) (n : ℤ) : l.sum % n = (l.map (· % n)).sum % n := by
induction l <;> simp [Int.add_emod, *]
lemma prod_int_mod (l : List ℤ) (n : ℤ) : l.prod % n = (l.map (· % n)).prod % n := by
induction l <;> simp [Int.mul_emod, *]
end List
section MonoidHom
variable [Monoid M] [Monoid N]
@[to_additive]
theorem map_list_prod {F : Type*} [FunLike F M N] [MonoidHomClass F M N] (f : F) (l : List M) :
f l.prod = (l.map f).prod :=
(l.prod_hom f).symm
namespace MonoidHom
@[to_additive]
protected theorem map_list_prod (f : M →* N) (l : List M) : f l.prod = (l.map f).prod :=
map_list_prod f l
end MonoidHom
end MonoidHom
namespace List
theorem prod_zpow {β : Type*} [DivisionCommMonoid β] {r : ℤ} {l : List β} :
l.prod ^ r = (map (fun x ↦ x ^ r) l).prod :=
let fr : β →* β := ⟨⟨fun b ↦ b ^ r, one_zpow r⟩, (mul_zpow · · r)⟩
map_list_prod fr l
/-- In a flatten, taking the first elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the flatten of the first `i` sublists. -/
lemma take_sum_flatten (L : List (List α)) (i : ℕ) :
L.flatten.take ((L.map length).take i).sum = (L.take i).flatten := by
induction L generalizing i
· simp
· cases i <;> simp [take_length_add_append, *]
/-- In a flatten, dropping all the elements up to an index which is the sum of the lengths of the
first `i` sublists, is the same as taking the join after dropping the first `i` sublists. -/
lemma drop_sum_flatten (L : List (List α)) (i : ℕ) :
L.flatten.drop ((L.map length).take i).sum = (L.drop i).flatten := by
induction L generalizing i
· simp
· cases i <;> simp [*]
end List
namespace List
/-- If all elements in a list are bounded below by `1`, then the length of the list is bounded
by the sum of the elements. -/
theorem length_le_sum_of_one_le (L : List ℕ) (h : ∀ i ∈ L, 1 ≤ i) : L.length ≤ L.sum := by
induction L with
| nil => simp
| cons j L IH =>
rw [sum_cons, length, add_comm]
exact Nat.add_le_add (h _ mem_cons_self) (IH fun i hi => h i (mem_cons.2 (Or.inr hi)))
end List