Skip to content

Commit f443f9d

Browse files
TanebgallaisMatthewDaggitt
authored
Add prime factorization and its properties (#1969)
* Add prime factorization and its properties * Add missing header comment I'd missed this when copy-pasting from my old code in a separate repo * Remove completely trivial lemma * Use equational reasoning in quotient|n proof * Fix typo in module header * Factorization => Factorisation * Use Nat lemma in extend-|/ * [ cleanup ] part of the proof * [ cleanup ] finishing up the job * [ cleanup ] a little bit more * [ cleanup ] the import list * [ fix ] header style * [ fix ] broken merge: missing import * Move Data.Nat.Rough to Data.Nat.Primality.Rough * Rename productPreserves↭⇒≡ to product-↭ * Use proof of Prime=>NonZero * Open reasoning once in factoriseRec * Rename Factorisation => PrimeFactorisation * Move wheres around * Tidy up Rough a bit * Move quotient|n to top of file * Replace factorisationPullToFront with slightly more generally useful factorisationHasAllPrimeFactors and a bit of logic * Fix import after merge * Clean up proof of 2-rough-n * Make argument to 2-rough-n implicit * Rename 2-rough-n to 2-rough * Complete merge, rewrite factorisation logic a bit Rewrite partially based on suggestions from James McKinna * Short circuit when candidate is greater than square root of product * Remove redefined lemma * Minor simplifications * Remove private pattern synonyms * Change name of lemma * Typo * Remove using list from import It feels like we're importing half the module anyway * Clean up proof * Fixes after merge * Addressed some feedback * Fix previous merge --------- Co-authored-by: Guillaume Allais <[email protected]> Co-authored-by: MatthewDaggitt <[email protected]>
1 parent eb16d8d commit f443f9d

File tree

4 files changed

+267
-5
lines changed

4 files changed

+267
-5
lines changed

CHANGELOG.md

+22-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,16 @@ Deprecated names
5353

5454
New modules
5555
-----------
56-
* `Algebra.Module.Bundles.Raw`: raw bundles for module-like algebraic structures
56+
57+
* Raw bundles for module-like algebraic structures:
58+
```
59+
Algebra.Module.Bundles.Raw
60+
```
61+
62+
* Prime factorisation of natural numbers.
63+
```
64+
Data.Nat.Primality.Factorisation
65+
```
5766

5867
* Consequences of 'infinite descent' for (accessible elements of) well-founded relations:
5968
```agda
@@ -296,6 +305,18 @@ Additions to existing modules
296305
pred-injective : .{{NonZero m}} → .{{NonZero n}} → pred m ≡ pred n → m ≡ n
297306
pred-cancel-≡ : pred m ≡ pred n → ((m ≡ 0 × n ≡ 1) ⊎ (m ≡ 1 × n ≡ 0)) ⊎ m ≡ n
298307
```
308+
309+
* Added new proofs to `Data.Nat.Primality`:
310+
```agda
311+
rough∧square>⇒prime : .{{NonTrivial n}} → m Rough n → m * m > n → Prime n
312+
productOfPrimes≢0 : All Prime as → NonZero (product as)
313+
productOfPrimes≥1 : All Prime as → product as ≥ 1
314+
```
315+
316+
* Added new proofs to `Data.List.Relation.Binary.Permutation.Propositional.Properties`:
317+
```agda
318+
product-↭ : product Preserves _↭_ ⟶ _≡_
319+
```
299320

300321
* Added new functions in `Data.String.Base`:
301322
```agda

src/Data/List/Relation/Binary/Permutation/Propositional/Properties.agda

+24-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ open import Algebra.Bundles
1212
open import Algebra.Definitions
1313
open import Algebra.Structures
1414
open import Data.Bool.Base using (Bool; true; false)
15-
open import Data.Nat using (suc)
15+
open import Data.Nat.Base using (suc; _*_)
16+
open import Data.Nat.Properties using (*-assoc; *-comm)
1617
open import Data.Product.Base using (-,_; proj₂)
1718
open import Data.List.Base as List
1819
open import Data.List.Relation.Binary.Permutation.Propositional
@@ -25,14 +26,13 @@ open import Data.Product.Base using (_,_; _×_; ∃; ∃₂)
2526
open import Function.Base using (_∘_; _⟨_⟩_)
2627
open import Level using (Level)
2728
open import Relation.Unary using (Pred)
28-
open import Relation.Binary.Core using (Rel; _Preserves₂_⟶_⟶_)
29+
open import Relation.Binary.Core using (Rel; _Preserves_⟶_; _Preserves₂_⟶_⟶_)
2930
open import Relation.Binary.Definitions using (_Respects_; Decidable)
3031
open import Relation.Binary.PropositionalEquality.Core as ≡
3132
using (_≡_ ; refl ; cong; cong₂; _≢_)
33+
open import Relation.Binary.PropositionalEquality.Properties using (module ≡-Reasoning)
3234
open import Relation.Nullary
3335

34-
open PermutationReasoning
35-
3636
private
3737
variable
3838
a b p : Level
@@ -172,6 +172,7 @@ shift v (x ∷ xs) ys = begin
172172
x ∷ (xs ++ [ v ] ++ ys) <⟨ shift v xs ys ⟩
173173
x ∷ v ∷ xs ++ ys <<⟨ refl ⟩
174174
v ∷ x ∷ xs ++ ys ∎
175+
where open PermutationReasoning
175176

176177
drop-mid-≡ : {x : A} ws xs {ys} {zs}
177178
ws ++ [ x ] ++ ys ≡ xs ++ [ x ] ++ zs
@@ -216,11 +217,13 @@ drop-mid {A = A} {x} ws xs p = drop-mid′ p ws xs refl refl
216217
_ ∷ (xs ++ _ ∷ _) <⟨ shift _ _ _ ⟩
217218
_ ∷ _ ∷ xs ++ _ <<⟨ refl ⟩
218219
_ ∷ _ ∷ xs ++ _ ∎
220+
where open PermutationReasoning
219221
drop-mid′ (swap y z p) (y ∷ z ∷ ws) (z ∷ []) refl refl = begin
220222
_ ∷ _ ∷ ws ++ _ <<⟨ refl ⟩
221223
_ ∷ (_ ∷ ws ++ _) <⟨ ↭-sym (shift _ _ _) ⟩
222224
_ ∷ (ws ++ _ ∷ _) <⟨ p ⟩
223225
_ ∷ _ ∎
226+
where open PermutationReasoning
224227
drop-mid′ (swap y z p) (y ∷ z ∷ ws) (z ∷ y ∷ xs) refl refl = swap y z (drop-mid′ p _ _ refl refl)
225228
drop-mid′ (trans p₁ p₂) ws xs refl refl with ∈-∃++ (∈-resp-↭ p₁ (∈-insert ws))
226229
... | (h , t , refl) = trans (drop-mid′ p₁ ws h refl refl) (drop-mid′ p₂ h xs refl refl)
@@ -245,6 +248,7 @@ drop-mid {A = A} {x} ws xs p = drop-mid′ p ws xs refl refl
245248
x ∷ xs ++ ys <⟨ ++-comm xs ys ⟩
246249
x ∷ ys ++ xs ↭⟨ shift x ys xs ⟨
247250
ys ++ (x ∷ xs) ∎
251+
where open PermutationReasoning
248252

249253
++-isMagma : IsMagma {A = List A} _↭_ _++_
250254
++-isMagma = record
@@ -300,6 +304,7 @@ shifts xs ys {zs} = begin
300304
(xs ++ ys) ++ zs ↭⟨ ++⁺ʳ zs (++-comm xs ys) ⟩
301305
(ys ++ xs) ++ zs ↭⟨ ++-assoc ys xs zs ⟩
302306
ys ++ xs ++ zs ∎
307+
where open PermutationReasoning
303308

304309
------------------------------------------------------------------------
305310
-- _∷_
@@ -315,6 +320,7 @@ drop-∷ = drop-mid [] []
315320
xs ++ [ x ] ↭⟨ shift x xs [] ⟩
316321
x ∷ xs ++ [] ≡⟨ Lₚ.++-identityʳ _ ⟩
317322
x ∷ xs ∎)
323+
where open PermutationReasoning
318324

319325
------------------------------------------------------------------------
320326
-- ʳ++
@@ -353,3 +359,17 @@ module _ {ℓ} {R : Rel A ℓ} (R? : Decidable R) where
353359
(x ∷ xs) ++ y ∷ ys ≡⟨ Lₚ.++-assoc [ x ] xs (y ∷ ys) ⟨
354360
x ∷ xs ++ y ∷ ys ∎
355361
where open PermutationReasoning
362+
363+
------------------------------------------------------------------------
364+
-- product
365+
366+
product-↭ : product Preserves _↭_ ⟶ _≡_
367+
product-↭ refl = refl
368+
product-↭ (prep x r) = cong (x *_) (product-↭ r)
369+
product-↭ (trans r s) = ≡.trans (product-↭ r) (product-↭ s)
370+
product-↭ (swap {xs} {ys} x y r) = begin
371+
x * (y * product xs) ≡˘⟨ *-assoc x y (product xs) ⟩
372+
(x * y) * product xs ≡⟨ cong₂ _*_ (*-comm x y) (product-↭ r) ⟩
373+
(y * x) * product ys ≡⟨ *-assoc y x (product ys) ⟩
374+
y * (x * product ys) ∎
375+
where open ≡-Reasoning

src/Data/Nat/Primality.agda

+23
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
module Data.Nat.Primality where
1010

11+
open import Data.List.Base using ([]; _∷_; product)
12+
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
1113
open import Data.Nat.Base
1214
open import Data.Nat.Divisibility
1315
open import Data.Nat.GCD using (module GCD; module Bézout)
@@ -294,6 +296,17 @@ prime⇒rough (prime pr) = pr
294296
rough∧∣⇒prime : .{{NonTrivial p}} p Rough n p ∣ n Prime p
295297
rough∧∣⇒prime r p∣n = prime (rough∧∣⇒rough r p∣n)
296298

299+
-- If a number n is m-rough, and m * m > n, then n must be prime.
300+
rough∧square>⇒prime : .{{NonTrivial n}} m Rough n m * m > n Prime n
301+
rough∧square>⇒prime rough m*m>n = prime ¬composite
302+
where
303+
¬composite : ¬ Composite _
304+
¬composite (composite d<n d∣n) = contradiction (m∣n⇒n≡quotient*m d∣n)
305+
(<⇒≢ (<-≤-trans m*m>n (*-mono-≤
306+
(rough⇒≤ (rough∧∣⇒rough rough (quotient-∣ d∣n)))
307+
(rough⇒≤ (rough∧∣⇒rough rough d∣n)))))
308+
where instance _ = n>1⇒nonTrivial (quotient>1 d∣n d<n)
309+
297310
-- Relationship between compositeness and primality.
298311
composite⇒¬prime : Composite n ¬ Prime n
299312
composite⇒¬prime composite[d] (prime p) = p composite[d]
@@ -309,6 +322,16 @@ prime⇒¬composite (prime p) = p
309322
¬prime⇒composite {n} ¬prime[n] =
310323
decidable-stable (composite? n) (¬prime[n] ∘′ ¬composite⇒prime)
311324

325+
productOfPrimes≢0 : {as} All Prime as NonZero (product as)
326+
productOfPrimes≢0 pas = product≢0 (All.map prime⇒nonZero pas)
327+
where
328+
product≢0 : {ns} All NonZero ns NonZero (product ns)
329+
product≢0 [] = _
330+
product≢0 {n ∷ ns} (nzn ∷ nzns) = m*n≢0 n _ {{nzn}} {{product≢0 nzns}}
331+
332+
productOfPrimes≥1 : {as} All Prime as product as ≥ 1
333+
productOfPrimes≥1 {as} pas = >-nonZero⁻¹ _ {{productOfPrimes≢0 pas}}
334+
312335
------------------------------------------------------------------------
313336
-- Basic (counter-)examples of Irreducible
314337

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
------------------------------------------------------------------------
2+
-- The Agda standard library
3+
--
4+
-- Prime factorisation of natural numbers and its properties
5+
------------------------------------------------------------------------
6+
7+
{-# OPTIONS --cubical-compatible --safe #-}
8+
9+
module Data.Nat.Primality.Factorisation where
10+
11+
open import Data.Empty using (⊥-elim)
12+
open import Data.Nat.Base
13+
open import Data.Nat.Divisibility
14+
open import Data.Nat.Properties
15+
open import Data.Nat.Induction using (<-Rec; <-rec; <-recBuilder)
16+
open import Data.Nat.Primality
17+
open import Data.Product as Π using (∃-syntax; _×_; _,_; proj₁; proj₂)
18+
open import Data.List.Base using (List; []; _∷_; _++_; product)
19+
open import Data.List.Membership.Propositional using (_∈_)
20+
open import Data.List.Membership.Propositional.Properties using (∈-∃++)
21+
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
22+
open import Data.List.Relation.Unary.Any using (here; there)
23+
open import Data.List.Relation.Binary.Permutation.Propositional
24+
using (_↭_; prep; swap; ↭-reflexive; ↭-refl; ↭-trans; refl; module PermutationReasoning)
25+
open import Data.List.Relation.Binary.Permutation.Propositional.Properties using (product-↭; All-resp-↭; shift)
26+
open import Data.Sum.Base using (inj₁; inj₂)
27+
open import Function.Base using (_$_; _∘_; _|>_; flip)
28+
open import Induction using (build)
29+
open import Induction.Lexicographic using (_⊗_; [_⊗_])
30+
open import Relation.Nullary.Decidable using (yes; no)
31+
open import Relation.Nullary.Negation using (contradiction)
32+
open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; trans; cong; module ≡-Reasoning)
33+
34+
private
35+
variable
36+
n :
37+
38+
------------------------------------------------------------------------
39+
-- Core definition
40+
41+
record PrimeFactorisation (n : ℕ) : Set where
42+
field
43+
factors : List ℕ
44+
isFactorisation : n ≡ product factors
45+
factorsPrime : All Prime factors
46+
47+
open PrimeFactorisation public using (factors)
48+
open PrimeFactorisation
49+
50+
------------------------------------------------------------------------
51+
-- Finding a factorisation
52+
53+
primeFactorisation[1] : PrimeFactorisation 1
54+
primeFactorisation[1] = record
55+
{ factors = []
56+
; isFactorisation = refl
57+
; factorsPrime = []
58+
}
59+
60+
primeFactorisation[p] : Prime n PrimeFactorisation n
61+
primeFactorisation[p] {n} pr = record
62+
{ factors = n ∷ []
63+
; isFactorisation = sym (*-identityʳ n)
64+
; factorsPrime = pr ∷ []
65+
}
66+
67+
-- This builds up three important things:
68+
-- * a proof that every number we've gotten to so far has increasingly higher
69+
-- possible least prime factor, so we don't have to repeat smaller factors
70+
-- over and over (this is the "m" and "rough" parameters)
71+
-- * a witness that this limit is getting closer to the number of interest, in a
72+
-- way that helps the termination checker (the "k" and "eq" parameters)
73+
-- * a proof that we can factorise any smaller number, which is useful when we
74+
-- encounter a factor, as we can then divide by that factor and continue from
75+
-- there without termination issues
76+
factorise : n .{{NonZero n}} PrimeFactorisation n
77+
factorise 1 = primeFactorisation[1]
78+
factorise n₀@(2+ _) = build [ <-recBuilder ⊗ <-recBuilder ] P facRec (n₀ , suc n₀ ∸ 4) 2-rough refl
79+
where
80+
P : ℕ × ℕ Set
81+
P (n , k) = {m} .{{NonTrivial n}} .{{NonTrivial m}} m Rough n suc n ∸ m * m ≡ k PrimeFactorisation n
82+
83+
facRec : n×k (<-Rec ⊗ <-Rec) P n×k P n×k
84+
facRec (n , zero) _ rough eq =
85+
-- Case 1: m * m > n, ∴ Prime n
86+
primeFactorisation[p] (rough∧square>⇒prime rough (m∸n≡0⇒m≤n eq))
87+
facRec (n@(2+ _) , suc k) (recFactor , recQuotient) {m@(2+ _)} rough eq with m ∣? n
88+
-- Case 2: m ∤ n, try larger m, reducing k accordingly
89+
... | no m∤n = recFactor (≤-<-trans (m∸n≤m k (m + m)) (n<1+n k)) {suc m} (∤⇒rough-suc m∤n rough) $ begin
90+
suc n ∸ (suc m + m * suc m) ≡⟨ cong (λ # suc n ∸ (suc m + #)) (*-suc m m) ⟩
91+
suc n ∸ (suc m + (m + m * m)) ≡⟨ cong (suc n ∸_) (+-assoc (suc m) m (m * m)) ⟨
92+
suc n ∸ (suc (m + m) + m * m) ≡⟨ cong (suc n ∸_) (+-comm (suc (m + m)) (m * m)) ⟩
93+
suc n ∸ (m * m + suc (m + m)) ≡⟨ ∸-+-assoc (suc n) (m * m) (suc (m + m)) ⟨
94+
(suc n ∸ m * m) ∸ suc (m + m) ≡⟨ cong (_∸ suc (m + m)) eq ⟩
95+
suc k ∸ suc (m + m) ∎
96+
where open ≡-Reasoning
97+
-- Case 3: m ∣ n, record m and recurse on the quotient
98+
... | yes m∣n = record
99+
{ factors = m ∷ ps
100+
; isFactorisation = sym m*Πps≡n
101+
; factorsPrime = rough∧∣⇒prime rough m∣n ∷ primes
102+
}
103+
where
104+
m<n : m < n
105+
m<n = begin-strict
106+
m <⟨ s≤s (≤-trans (m≤n+m m _) (+-monoʳ-≤ _ (m≤m+n m _))) ⟩
107+
pred (m * m) <⟨ s<s⁻¹ (m∸n≢0⇒n<m λ eq′ 0≢1+n (trans (sym eq′) eq)) ⟩
108+
n ∎
109+
where open ≤-Reasoning
110+
111+
q = quotient m∣n
112+
113+
instance _ = n>1⇒nonTrivial (quotient>1 m∣n m<n)
114+
115+
factorisation[q] : PrimeFactorisation q
116+
factorisation[q] = recQuotient (quotient-< m∣n) (suc q ∸ m * m) (rough∧∣⇒rough rough (quotient-∣ m∣n)) refl
117+
118+
ps = factors factorisation[q]
119+
120+
primes = factorsPrime factorisation[q]
121+
122+
m*Πps≡n : m * product ps ≡ n
123+
m*Πps≡n = begin
124+
m * product ps ≡⟨ cong (m *_) (isFactorisation factorisation[q]) ⟨
125+
m * q ≡⟨ m∣n⇒n≡m*quotient m∣n ⟨
126+
n ∎
127+
where open ≡-Reasoning
128+
129+
------------------------------------------------------------------------
130+
-- Properties of a factorisation
131+
132+
factorisationHasAllPrimeFactors : {as} {p} Prime p p ∣ product as All Prime as p ∈ as
133+
factorisationHasAllPrimeFactors {[]} {2+ p} pPrime p∣Πas [] = contradiction (∣1⇒≡1 p∣Πas) λ ()
134+
factorisationHasAllPrimeFactors {a ∷ as} {p} pPrime p∣aΠas (aPrime ∷ asPrime) with euclidsLemma a (product as) pPrime p∣aΠas
135+
... | inj₂ p∣Πas = there (factorisationHasAllPrimeFactors pPrime p∣Πas asPrime)
136+
... | inj₁ p∣a with prime⇒irreducible aPrime p∣a
137+
... | inj₁ refl = contradiction pPrime ¬prime[1]
138+
... | inj₂ refl = here refl
139+
140+
private
141+
factorisationUnique′ : (as bs : List ℕ) product as ≡ product bs All Prime as All Prime bs as ↭ bs
142+
factorisationUnique′ [] [] Πas≡Πbs asPrime bsPrime = refl
143+
factorisationUnique′ [] (b@(2+ _) ∷ bs) Πas≡Πbs prime[as] (_ ∷ prime[bs]) =
144+
contradiction Πas≡Πbs (<⇒≢ Πas<Πbs)
145+
where
146+
Πas<Πbs : product [] < product (b ∷ bs)
147+
Πas<Πbs = begin-strict
148+
1 ≡⟨⟩
149+
1 * 1 <⟨ *-monoˡ-< 1 {1} {b} sz<ss ⟩
150+
b * 1 ≤⟨ *-monoʳ-≤ b (productOfPrimes≥1 prime[bs]) ⟩
151+
b * product bs ≡⟨⟩
152+
product (b ∷ bs) ∎
153+
where open ≤-Reasoning
154+
155+
factorisationUnique′ (a ∷ as) bs Πas≡Πbs (prime[a] ∷ prime[as]) prime[bs] = a∷as↭bs
156+
where
157+
a∣Πbs : a ∣ product bs
158+
a∣Πbs = divides (product as) $ begin
159+
product bs ≡⟨ Πas≡Πbs ⟨
160+
product (a ∷ as) ≡⟨⟩
161+
a * product as ≡⟨ *-comm a (product as) ⟩
162+
product as * a ∎
163+
where open ≡-Reasoning
164+
165+
shuffle : ∃[ bs′ ] bs ↭ a ∷ bs′
166+
shuffle with ys , zs , p ∈-∃++ (factorisationHasAllPrimeFactors prime[a] a∣Πbs prime[bs])
167+
= ys ++ zs , ↭-trans (↭-reflexive p) (shift a ys zs)
168+
169+
bs′ = proj₁ shuffle
170+
bs↭a∷bs′ = proj₂ shuffle
171+
172+
Πas≡Πbs′ : product as ≡ product bs′
173+
Πas≡Πbs′ = *-cancelˡ-≡ (product as) (product bs′) a {{prime⇒nonZero prime[a]}} $ begin
174+
a * product as ≡⟨ Πas≡Πbs ⟩
175+
product bs ≡⟨ product-↭ bs↭a∷bs′ ⟩
176+
a * product bs′ ∎
177+
where open ≡-Reasoning
178+
179+
prime[bs'] : All Prime bs′
180+
prime[bs'] = All.tail (All-resp-↭ bs↭a∷bs′ prime[bs])
181+
182+
a∷as↭bs : a ∷ as ↭ bs
183+
a∷as↭bs = begin
184+
a ∷ as <⟨ factorisationUnique′ as bs′ Πas≡Πbs′ prime[as] prime[bs'] ⟩
185+
a ∷ bs′ ↭⟨ bs↭a∷bs′ ⟨
186+
bs ∎
187+
where open PermutationReasoning
188+
189+
factorisationUnique : (f f′ : PrimeFactorisation n) factors f ↭ factors f′
190+
factorisationUnique {n} f f′ =
191+
factorisationUnique′ (factors f) (factors f′) Πf≡Πf′ (factorsPrime f) (factorsPrime f′)
192+
where
193+
Πf≡Πf′ : product (factors f) ≡ product (factors f′)
194+
Πf≡Πf′ = begin
195+
product (factors f) ≡⟨ isFactorisation f ⟨
196+
n ≡⟨ isFactorisation f′ ⟩
197+
product (factors f′) ∎
198+
where open ≡-Reasoning

0 commit comments

Comments
 (0)