Skip to content

Commit 063bc4d

Browse files
committed
feat(partitions): generalized pentagonal numbers.
1 parent 50468bb commit 063bc4d

1 file changed

Lines changed: 268 additions & 0 deletions

File tree

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
/-
2+
Copyright (c) 2025 Beibei Xiong.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Beibei Xiong
5+
-/
6+
import Mathlib.Algebra.Order.Ring.Star
7+
import Mathlib.Analysis.Normed.Ring.Lemmas
8+
import Mathlib.Combinatorics.Enumerative.Partition
9+
import Mathlib.Data.Int.Star
10+
import Mathlib.RingTheory.PowerSeries.Basic
11+
12+
/-!
13+
# Pentagonal numbers, parity of strict partitions, and a finite product cut-off
14+
15+
This file introduces:
16+
* the (integer-valued) generalized pentagonal numbers `generalizedPentagonalZ`,
17+
* the parity difference of strict partitions of `n`:
18+
`strict_partitions_parity_diff n = p_even_distinct n - p_odd_distinct n`,
19+
* a finite product `E n` approximating Euler's generating function, and a simple lemma about its
20+
coefficient extraction being a restatement with the same cut-off.
21+
22+
These pieces are intended to support proofs around Euler's pentagonal number theorem and
23+
related identities.
24+
25+
## Main definitions
26+
27+
* `p_even_distinct n` : number of partitions of `n` into distinct parts with an even
28+
number of parts.
29+
* `p_odd_distinct n` : number of partitions of `n` into distinct parts with an odd
30+
number of parts.
31+
* `generalizedPentagonalZ j` : the (possibly negative-indexed) generalized pentagonal numbers
32+
`j*(3*j - 1)/2` as integers.
33+
* `generalizedPentagonalNum j` : the same, coerced to `ℕ` by `Int.toNat`.
34+
* `error_term_e n` : the Euler error-term `eₙ`, which is `(-1)^{|j|}` if `n` equals a generalized
35+
pentagonal number for some `j : ℤ`, and `0` otherwise.
36+
* `strict_partitions_parity_diff n` : `(p_even_distinct n : ℤ) - (p_odd_distinct n : ℤ)`.
37+
* `E n` : a finite product `∏_{k=1}^{n+1} (1 - X^k)` seen as a `PowerSeries ℤ`.
38+
39+
## Implementation details
40+
41+
* We keep an integer-valued definition `generalizedPentagonalZ : ℤ → ℤ` for convenience in
42+
arithmetic facts and sign arguments. The natural-number version `generalizedPentagonalNum`
43+
is just `Int.toNat` of the integer-valued one.
44+
* A bounded search for witnesses `j` such that `n = generalizedPentagonalZ j` is provided by
45+
`exists_j_iff_exists_j_in_range`.
46+
47+
## TODO
48+
49+
* Connect `error_term_e` and `strict_partitions_parity_diff` via Euler's pentagonal theorem.
50+
* Provide API lemmas for coefficients of the infinite product and its finite truncations.
51+
52+
## Tags
53+
54+
partition, pentagonal number, Euler, generating function
55+
56+
## References
57+
58+
* <https://en.wikipedia.org/wiki/Pentagonal_number_theorem>
59+
-/
60+
61+
open Finset Nat Int Partition PowerSeries Polynomial
62+
open scoped BigOperators
63+
64+
/--
65+
`p_even_distinct n`: number of partitions of `n` into distinct parts with an even number of parts.
66+
-/
67+
def p_even_distinct (n : ℕ) : ℕ :=
68+
((Nat.Partition.distincts n).filter (fun p => Even p.parts.card)).card
69+
70+
/--
71+
`p_odd_distinct n`: number of partitions of `n` into distinct parts with an odd number of parts.
72+
-/
73+
def p_odd_distinct (n : ℕ) : ℕ :=
74+
((Nat.Partition.distincts n).filter (fun p => Odd p.parts.card)).card
75+
76+
/-!
77+
### Generalized pentagonal numbers and the error term `eₙ`
78+
-/
79+
80+
/--
81+
Generalized pentagonal number (integer-valued): `j ↦ j * (3*j - 1) / 2`.
82+
-/
83+
def generalizedPentagonalZ (j : ℤ) : ℤ := j * (3 * j - 1) / 2
84+
85+
lemma gpn_nonneg (j : ℤ) : 0 ≤ generalizedPentagonalZ j := by
86+
-- Show `0 ≤ j * (3*j - 1)` and then apply `Int.ediv_nonneg`.
87+
have : 0 ≤ j * (3 * j - 1) := by
88+
rcases le_or_gt j 0 with hle | hgt
89+
· exact mul_nonneg_of_nonpos_of_nonpos hle (by linarith)
90+
· exact mul_nonneg hgt.le (by linarith)
91+
exact Int.ediv_nonneg this (by decide)
92+
93+
/--
94+
Natural-number version of generalized pentagonal numbers.
95+
-/
96+
def generalizedPentagonalNum (j : ℤ) : ℕ :=
97+
Int.toNat (generalizedPentagonalZ j)
98+
99+
lemma gpn_coe (j : ℤ) :
100+
(generalizedPentagonalNum j : ℤ) = generalizedPentagonalZ j := by
101+
simp [generalizedPentagonalNum, generalizedPentagonalZ]
102+
refine (ediv_nonneg_iff_of_pos ?_).mpr ?_
103+
· norm_num
104+
· by_cases hj : j > 0
105+
· refine Int.mul_nonneg ?_ ?_
106+
· linarith
107+
· linarith
108+
· have hle : j ≤ 0 := by linarith
109+
refine Int.mul_nonneg_of_nonpos_of_nonpos hle ?_
110+
linarith
111+
/--
112+
`2 ∣ (-j + 3*j^2)`.
113+
-/
114+
theorem even_neg_j_add_three_j_sq (j : ℤ) : 2 ∣ (-j + 3 * j ^ 2) := by
115+
-- Factorization: `-j + 3*j^2 = j*(3*j - 1)`.
116+
have h : -j + 3 * j ^ 2 = j * (3 * j - 1) := by ring
117+
rw [h]
118+
-- Rewrite as `j*(j-1) + 2*j^2`.
119+
have h' : j * (3 * j - 1) = j * (j - 1) + 2 * j ^ 2 := by ring
120+
rw [h']
121+
-- `2 ∣ j*(j-1)` since product of consecutive integers is even.
122+
have h_consec : 2 ∣ j * (j - 1) := by
123+
rcases Int.even_or_odd j with ⟨k, rfl⟩ | ⟨k, rfl⟩
124+
· use k * (2 * k - 1); ring
125+
· use k * (2 * k + 1); ring
126+
-- `2 ∣ 2*j^2` trivially.
127+
have h_twice : 22 * j ^ 2 := by use j ^ 2
128+
-- Sum of two even numbers is even.
129+
exact dvd_add h_consec h_twice
130+
131+
theorem even_neg_j_add_three_j_sq' (j : ℤ) : Even (-j + 3 * j ^ 2) := by
132+
have := even_neg_j_add_three_j_sq j
133+
exact (even_iff_exists_two_nsmul (-j + 3 * j ^ 2)).mpr this
134+
135+
/--
136+
Existence of `j` with `n = generalizedPentagonalZ j` can be restricted to a bounded interval
137+
`j ∈ [-(n+1), n+1]`.
138+
-/
139+
theorem exists_j_iff_exists_j_in_range (n : ℕ) :
140+
(∃ j : ℤ, n = generalizedPentagonalZ j) ↔
141+
(∃ j ∈ Finset.Icc (-(n+1 : ℤ)) ((n+1 : ℤ)), n = generalizedPentagonalZ j) := by
142+
constructor
143+
· rintro ⟨j, hn⟩
144+
use j
145+
constructor
146+
· refine mem_Icc.mpr ?_
147+
rw [hn, generalizedPentagonalZ]
148+
have h_bound1 : -(j * (3 * j - 1) / 2 + 1) ≤ j := by
149+
have h_nezero : (2 : ℤ) ≠ 0 := by norm_num
150+
refine Int.add_nonneg_iff_neg_le.mp ?_
151+
· by_cases hj : j > 0
152+
· refine Int.add_nonneg ?_ ?_
153+
linarith
154+
refine Int.add_nonneg ?_ ?_
155+
refine (ediv_nonneg_iff_of_pos ?_).mpr ?_
156+
linarith
157+
refine Int.mul_nonneg ?_ ?_
158+
linarith
159+
linarith
160+
linarith
161+
· have hle : j ≤ 0 := by linarith
162+
by_cases hj1 : j = 0
163+
· rw [hj1]
164+
norm_num
165+
· have hj2 : j < 0 := by omega
166+
have h_bound1 : j + j * (3 * j - 1) / 2 = j * (3 * j + 1) / 2 := by
167+
have h_nezero : (2 : ℤ) ≠ 0 := by norm_num
168+
refine Int.eq_ediv_of_mul_eq_right h_nezero ?_
169+
ring_nf
170+
rw [mul_two ,add_assoc]
171+
congr 1
172+
rw [mul_comm _ 3]
173+
have h_aux : (-j + 3 * j ^ 2) / 2 * 2 = -j + 3 * j ^ 2 := by
174+
apply Int.ediv_two_mul_two_of_even (even_neg_j_add_three_j_sq' j)
175+
rw [h_aux]
176+
ring
177+
178+
rw [← add_assoc, h_bound1]
179+
refine Int.add_nonneg ?_ ?_
180+
refine (ediv_nonneg_iff_of_pos ?_).mpr ?_
181+
omega
182+
have h: 0 < j * (3 * j + 1) := by
183+
refine Int.mul_pos_of_neg_of_neg hj2 ?_
184+
linarith
185+
linarith
186+
omega
187+
188+
have h_bound2 : j ≤ j * (3 * j - 1) / 2 + 1 := by
189+
have h_nezero : (2 : ℤ) ≠ 0 := by norm_num
190+
refine Int.sub_nonneg.mp ?_
191+
have h_aux : j * (3 * j - 1) / 2 + 1 - j = 3 * j * (j - 1) / 2 + 1 := by
192+
193+
ring_nf
194+
rw [sub_add]
195+
rw [sub_eq_add_neg]
196+
congr 1
197+
ring_nf
198+
refine Int.eq_ediv_of_mul_eq_right h_nezero ?_
199+
ring_nf
200+
have h_aux : (-j + 3 * j ^ 2) / 2 * 2 = -j + 3 * j ^ 2 := by
201+
apply Int.ediv_two_mul_two_of_even (even_neg_j_add_three_j_sq' j)
202+
rw [mul_comm _ 3, h_aux]
203+
ring
204+
205+
rw [h_aux]
206+
refine Int.add_nonneg ?_ ?_
207+
refine (ediv_nonneg_iff_of_pos ?_).mpr ?_
208+
omega
209+
by_cases hj : j < 0
210+
· have hj1 : j - 1 < 0 := by linarith
211+
have h_mul_neg : j * (j - 1) > 0 := by
212+
refine Int.mul_pos_of_neg_of_neg hj hj1
213+
linarith
214+
· have hle : j ≥ 0 := by linarith
215+
by_cases hj1 : j = 0
216+
· rw [hj1]
217+
norm_num
218+
· have hj2 : j > 0 := by omega
219+
have h_mul_pos : 3 * j * (j - 1) ≥ 0 := by
220+
refine Int.mul_nonneg ?_ ?_
221+
linarith
222+
linarith
223+
linarith
224+
linarith
225+
exact ⟨h_bound1, h_bound2⟩
226+
227+
· exact hn
228+
· rintro ⟨j, _, hn⟩
229+
use j, hn
230+
231+
/--
232+
Decidability of `∃ j : ℤ, n = generalizedPentagonalZ j` via a bounded search window.
233+
-/
234+
instance (n : ℕ) : Decidable (∃ j : ℤ, n = generalizedPentagonalZ j) :=
235+
decidable_of_iff'
236+
(∃ j ∈ Finset.Icc (-(n + 1 : ℤ)) (n + 1 : ℤ), n = generalizedPentagonalZ j)
237+
(exists_j_iff_exists_j_in_range n)
238+
239+
/--
240+
Euler's error term `eₙ`. It is `(-1)^{|j|}` if `n` is a generalized pentagonal number
241+
`generalizedPentagonalZ j` for some `j : ℤ`, and `0` otherwise.
242+
-/
243+
noncomputable def error_term_e (n : ℕ) : ℤ :=
244+
if h : ∃ j : ℤ, n = generalizedPentagonalZ j then
245+
let j := Classical.choose h
246+
(-1 : ℤ) ^ j.natAbs
247+
else
248+
0
249+
250+
/--
251+
Difference between the numbers of strict partitions with even vs. odd number of parts.
252+
-/
253+
def strict_partitions_parity_diff (n : ℕ) : ℤ :=
254+
(p_even_distinct n : ℤ) - (p_odd_distinct n : ℤ)
255+
256+
/--
257+
Finite product `E(n) = ∏_{k=1}^{n+1} (1 - X^k)` as a `PowerSeries ℤ`.
258+
-/
259+
noncomputable def E (n : ℕ) : PowerSeries ℤ :=
260+
∏ k ∈ Finset.range (n + 1), (1 - (PowerSeries.X : PowerSeries ℤ) ^ (k + 1))
261+
262+
lemma coeff_E_eq_coeff_cutoff (n : ℕ) :
263+
(PowerSeries.coeff n) (E n) =
264+
(PowerSeries.coeff n)
265+
(∏ k ∈ Finset.range (n + 1), (1 - (PowerSeries.X : PowerSeries ℤ) ^ (k + 1))) := by
266+
simp [E]
267+
268+

0 commit comments

Comments
 (0)