Skip to content

Commit ebabd04

Browse files
Parcly-Taxelurkud
andcommitted
feat: counting elements in an interval with given residue (#9348)
Co-authored-by: Yury G. Kudryashov <urkud@urkud.name>
1 parent b90c18e commit ebabd04

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

Mathlib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,7 @@ import Mathlib.Data.Int.AbsoluteValue
17201720
import Mathlib.Data.Int.Associated
17211721
import Mathlib.Data.Int.Basic
17221722
import Mathlib.Data.Int.Bitwise
1723+
import Mathlib.Data.Int.CardIntervalMod
17231724
import Mathlib.Data.Int.Cast.Basic
17241725
import Mathlib.Data.Int.Cast.Defs
17251726
import Mathlib.Data.Int.Cast.Field

Mathlib/Data/Int/CardIntervalMod.lean

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/-
2+
Copyright (c) 2024 Jeremy Tan. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Jeremy Tan
5+
-/
6+
import Mathlib.Data.Int.Interval
7+
import Mathlib.Data.Int.ModEq
8+
import Mathlib.Data.Nat.Count
9+
import Mathlib.Data.Nat.Interval
10+
import Mathlib.Data.Rat.Floor
11+
12+
/-!
13+
# Counting elements in an interval with given residue
14+
15+
The theorems in this file generalise `Nat.card_multiples` in `Data.Nat.Factorization.Basic` to
16+
all integer intervals and any fixed residue (not just zero, which reduces to the multiples).
17+
Theorems are given for `Ico` and `Ioc` intervals.
18+
-/
19+
20+
21+
open Finset Int
22+
23+
namespace Int
24+
25+
variable (a b : ℤ) {r : ℤ} (hr : 0 < r)
26+
27+
lemma Ico_filter_dvd_eq : (Ico a b).filter (r ∣ ·) =
28+
(Ico ⌈a / (r : ℚ)⌉ ⌈b / (r : ℚ)⌉).map ⟨(· * r), mul_left_injective₀ hr.ne'⟩ := by
29+
ext x
30+
simp only [mem_map, mem_filter, mem_Ico, ceil_le, lt_ceil, div_le_iff, lt_div_iff,
31+
dvd_iff_exists_eq_mul_left, cast_pos.2 hr, ← cast_mul, cast_lt, cast_le]
32+
aesop
33+
34+
lemma Ioc_filter_dvd_eq : (Ioc a b).filter (r ∣ ·) =
35+
(Ioc ⌊a / (r : ℚ)⌋ ⌊b / (r : ℚ)⌋).map ⟨(· * r), mul_left_injective₀ hr.ne'⟩ := by
36+
ext x
37+
simp only [mem_map, mem_filter, mem_Ioc, floor_lt, le_floor, div_lt_iff, le_div_iff,
38+
dvd_iff_exists_eq_mul_left, cast_pos.2 hr, ← cast_mul, cast_lt, cast_le]
39+
aesop
40+
41+
/-- There are `⌈b / r⌉ - ⌈a / r⌉` multiples of `r` in `[a, b)`, if `a ≤ b`. -/
42+
theorem Ico_filter_dvd_card : ((Ico a b).filter (r ∣ ·)).card =
43+
max (⌈b / (r : ℚ)⌉ - ⌈a / (r : ℚ)⌉) 0 := by
44+
rw [Ico_filter_dvd_eq _ _ hr, card_map, card_Ico, toNat_eq_max]
45+
46+
/-- There are `⌊b / r⌋ - ⌊a / r⌋` multiples of `r` in `(a, b]`, if `a ≤ b`. -/
47+
theorem Ioc_filter_dvd_card : ((Ioc a b).filter (r ∣ ·)).card =
48+
max (⌊b / (r : ℚ)⌋ - ⌊a / (r : ℚ)⌋) 0 := by
49+
rw [Ioc_filter_dvd_eq _ _ hr, card_map, card_Ioc, toNat_eq_max]
50+
51+
lemma Ico_filter_modEq_eq (v : ℤ) : (Ico a b).filter (· ≡ v [ZMOD r]) =
52+
((Ico (a - v) (b - v)).filter (r ∣ ·)).map ⟨(· + v), add_left_injective v⟩ := by
53+
ext x
54+
simp_rw [mem_map, mem_filter, mem_Ico, Function.Embedding.coeFn_mk, ← eq_sub_iff_add_eq,
55+
exists_eq_right, modEq_comm, modEq_iff_dvd, sub_lt_sub_iff_right, sub_le_sub_iff_right]
56+
57+
lemma Ioc_filter_modEq_eq (v : ℤ) : (Ioc a b).filter (· ≡ v [ZMOD r]) =
58+
((Ioc (a - v) (b - v)).filter (r ∣ ·)).map ⟨(· + v), add_left_injective v⟩ := by
59+
ext x
60+
simp_rw [mem_map, mem_filter, mem_Ioc, Function.Embedding.coeFn_mk, ← eq_sub_iff_add_eq,
61+
exists_eq_right, modEq_comm, modEq_iff_dvd, sub_lt_sub_iff_right, sub_le_sub_iff_right]
62+
63+
/-- There are `⌈(b - v) / r⌉ - ⌈(a - v) / r⌉` numbers congruent to `v` mod `r` in `[a, b)`,
64+
if `a ≤ b`. -/
65+
theorem Ico_filter_modEq_card (v : ℤ) : ((Ico a b).filter (· ≡ v [ZMOD r])).card =
66+
max (⌈(b - v) / (r : ℚ)⌉ - ⌈(a - v) / (r : ℚ)⌉) 0 := by
67+
simp [Ico_filter_modEq_eq, Ico_filter_dvd_eq, toNat_eq_max, hr]
68+
69+
/-- There are `⌊(b - v) / r⌋ - ⌊(a - v) / r⌋` numbers congruent to `v` mod `r` in `(a, b]`,
70+
if `a ≤ b`. -/
71+
theorem Ioc_filter_modEq_card (v : ℤ) : ((Ioc a b).filter (· ≡ v [ZMOD r])).card =
72+
max (⌊(b - v) / (r : ℚ)⌋ - ⌊(a - v) / (r : ℚ)⌋) 0 := by
73+
simp [Ioc_filter_modEq_eq, Ioc_filter_dvd_eq, toNat_eq_max, hr]
74+
75+
end Int
76+
77+
namespace Nat
78+
79+
variable (a b : ℕ) {r : ℕ} (hr : 0 < r)
80+
81+
lemma Ico_filter_modEq_cast {v : ℕ} : ((Ico a b).filter (· ≡ v [MOD r])).map castEmbedding =
82+
(Ico ↑a ↑b).filter (· ≡ v [ZMOD r]) := by
83+
ext x
84+
simp only [mem_map, mem_filter, mem_Ico, castEmbedding_apply]
85+
constructor
86+
· simp_rw [forall_exists_index, ← coe_nat_modEq_iff]; intro y ⟨h, c⟩; subst c; exact_mod_cast h
87+
· intro h; lift x to ℕ using (by linarith); exact ⟨x, by simp_all [coe_nat_modEq_iff]⟩
88+
89+
lemma Ioc_filter_modEq_cast {v : ℕ} : ((Ioc a b).filter (· ≡ v [MOD r])).map castEmbedding =
90+
(Ioc ↑a ↑b).filter (· ≡ v [ZMOD r]) := by
91+
ext x
92+
simp only [mem_map, mem_filter, mem_Ioc, castEmbedding_apply]
93+
constructor
94+
· simp_rw [forall_exists_index, ← coe_nat_modEq_iff]; intro y ⟨h, c⟩; subst c; exact_mod_cast h
95+
· intro h; lift x to ℕ using (by linarith); exact ⟨x, by simp_all [coe_nat_modEq_iff]⟩
96+
97+
/-- There are `⌈(b - v) / r⌉ - ⌈(a - v) / r⌉` numbers congruent to `v` mod `r` in `[a, b)`,
98+
if `a ≤ b`. `Nat` version of `Int.Ico_filter_modEq_card`. -/
99+
theorem Ico_filter_modEq_card (v : ℕ) : ((Ico a b).filter (· ≡ v [MOD r])).card =
100+
max (⌈(b - v) / (r : ℚ)⌉ - ⌈(a - v) / (r : ℚ)⌉) 0 := by
101+
simp_rw [← Ico_filter_modEq_cast _ _ ▸ card_map _,
102+
Int.Ico_filter_modEq_card _ _ (cast_lt.mpr hr), Int.cast_ofNat]
103+
104+
/-- There are `⌊(b - v) / r⌋ - ⌊(a - v) / r⌋` numbers congruent to `v` mod `r` in `(a, b]`,
105+
if `a ≤ b`. `Nat` version of `Int.Ioc_filter_modEq_card`. -/
106+
theorem Ioc_filter_modEq_card (v : ℕ) : ((Ioc a b).filter (· ≡ v [MOD r])).card =
107+
max (⌊(b - v) / (r : ℚ)⌋ - ⌊(a - v) / (r : ℚ)⌋) 0 := by
108+
simp_rw [← Ioc_filter_modEq_cast _ _ ▸ card_map _,
109+
Int.Ioc_filter_modEq_card _ _ (cast_lt.mpr hr), Int.cast_ofNat]
110+
111+
/-- There are `⌈(b - v % r) / r⌉` numbers in `[0, b)` congruent to `v` mod `r`. -/
112+
theorem count_modEq_card_eq_ceil (v : ℕ) :
113+
b.count (· ≡ v [MOD r]) = ⌈(b - (v % r : ℕ)) / (r : ℚ)⌉ := by
114+
have hr' : 0 < (r : ℚ) := by positivity
115+
rw [count_eq_card_filter_range, ← Ico_zero_eq_range, Ico_filter_modEq_card _ _ hr,
116+
max_eq_left (sub_nonneg.mpr <| by gcongr <;> positivity)]
117+
conv_lhs =>
118+
rw [← div_add_mod v r, cast_add, cast_mul, add_comm]
119+
tactic => simp_rw [← sub_sub, sub_div (_ - _), mul_div_cancel_left _ hr'.ne', ceil_sub_nat]
120+
rw [sub_sub_sub_cancel_right, cast_zero, zero_sub]
121+
rw [sub_eq_self, ceil_eq_zero_iff, Set.mem_Ioc, div_le_iff hr', lt_div_iff hr', neg_one_mul,
122+
zero_mul, neg_lt_neg_iff, cast_lt]
123+
exact ⟨mod_lt _ hr, by simp⟩
124+
125+
/-- There are `b / r + [v % r < b % r]` numbers in `[0, b)` congruent to `v` mod `r`,
126+
where `[·]` is the Iverson bracket. -/
127+
theorem count_modEq_card (v : ℕ) :
128+
b.count (· ≡ v [MOD r]) = b / r + if v % r < b % r then 1 else 0 := by
129+
have hr' : 0 < (r : ℚ) := by positivity
130+
rw [← ofNat_inj, count_modEq_card_eq_ceil _ hr, cast_add]
131+
conv_lhs => rw [← div_add_mod b r, cast_add, cast_mul, ← add_sub, _root_.add_div,
132+
mul_div_cancel_left _ hr'.ne', add_comm, Int.ceil_add_nat, add_comm]
133+
rw [add_right_inj]
134+
split_ifs with h
135+
· rw [← cast_sub h.le, Int.ceil_eq_iff, div_le_iff hr', lt_div_iff hr', cast_one, Int.cast_one,
136+
sub_self, zero_mul, cast_pos, tsub_pos_iff_lt, one_mul, cast_le, tsub_le_iff_right]
137+
exact ⟨h, ((mod_lt _ hr).trans_le (by simp)).le⟩
138+
· rw [cast_zero, ceil_eq_zero_iff, Set.mem_Ioc, div_le_iff hr', lt_div_iff hr', zero_mul,
139+
tsub_nonpos, ← neg_eq_neg_one_mul, neg_lt_sub_iff_lt_add, ← cast_add, cast_lt, cast_le]
140+
exact ⟨(mod_lt _ hr).trans_le (by simp), not_lt.mp h⟩
141+
142+
end Nat

0 commit comments

Comments
 (0)