@@ -9,14 +9,17 @@ open import Cubical.Foundations.Isomorphism
99open import Cubical.Induction.WellFounded
1010
1111open import Cubical.Data.Sigma as Σ
12- open import Cubical.Data.Fin
12+ open import Cubical.Data.Fin as F hiding (_%_ ; _/_)
1313open import Cubical.Data.NatPlusOne
1414
1515open import Cubical.HITs.PropositionalTruncation as PropTrunc
1616
1717open import Cubical.Data.Nat.Base
1818open import Cubical.Data.Nat.Properties
1919open import Cubical.Data.Nat.Order
20+ open import Cubical.Data.Nat.Mod renaming (
21+ quotient'_/_ to _/_ ; remainder'_/_ to _%_
22+ ; ≡remainder'+quotient' to ≡%+·/ ; mod'< to %< )
2023open import Cubical.Data.Nat.Divisibility
2124
2225open import Cubical.Relation.Nullary
@@ -66,46 +69,94 @@ oneGCD m = symGCD (divsGCD (∣-oneˡ m))
6669zeroGCD : ∀ m → isGCD m 0 m
6770zeroGCD m = divsGCD (∣-zeroʳ m)
6871
72+ -- a pair of useful lemmas about ∣ and (efficient) %
6973private
70- lem₁ : prediv d (suc n) → prediv d (m % suc n) → prediv d m
71- lem₁ {d} {n} {m} (c₁ , p₁) (c₂ , p₂) = (q · c₁ + c₂) , p
72- where r = m % suc n; q = n%k≡n[modk] m (suc n) .fst
73- p = (q · c₁ + c₂) · d ≡⟨ sym (·-distribʳ (q · c₁) c₂ d) ⟩
74- (q · c₁) · d + c₂ · d ≡⟨ cong (_+ c₂ · d) (sym (·-assoc q c₁ d)) ⟩
75- q · (c₁ · d) + c₂ · d ≡[ i ]⟨ q · (p₁ i) + (p₂ i) ⟩
76- q · (suc n) + r ≡⟨ n%k≡n[modk] m (suc n) .snd ⟩
77- m ∎
78-
79- lem₂ : prediv d (suc n) → prediv d m → prediv d (m % suc n)
80- lem₂ {d} {n} {m} (c₁ , p₁) (c₂ , p₂) = c₂ ∸ q · c₁ , p
81- where r = m % suc n; q = n%k≡n[modk] m (suc n) .fst
82- p = (c₂ ∸ q · c₁) · d ≡⟨ ∸-distribʳ c₂ (q · c₁) d ⟩
83- c₂ · d ∸ (q · c₁) · d ≡⟨ cong (c₂ · d ∸_) (sym (·-assoc q c₁ d)) ⟩
84- c₂ · d ∸ q · (c₁ · d) ≡[ i ]⟨ p₂ i ∸ q · (p₁ i) ⟩
85- m ∸ q · (suc n) ≡⟨ cong (_∸ q · (suc n)) (sym (n%k≡n[modk] m (suc n) .snd)) ⟩
86- (q · (suc n) + r) ∸ q · (suc n) ≡⟨ cong (_∸ q · (suc n)) (+-comm (q · (suc n)) r) ⟩
87- (r + q · (suc n)) ∸ q · (suc n) ≡⟨ ∸-cancelʳ r zero (q · (suc n)) ⟩
88- r ∎
74+ ∣→∣% : ∀ {m n d} → d ∣ m → d ∣ n → d ∣ (m % n)
75+ ∣%→∣ : ∀ {m n d} → d ∣ (m % n) → d ∣ n → d ∣ m
76+
77+ ∣→∣% {m} {0 } {d} d∣m d∣n = d∣m
78+ ∣→∣% {m} {n@(suc _)} {d} d∣m d∣n =
79+ let
80+ k₁ = ∣-untrunc d∣m .fst
81+ k₁·d≡m = ∣-untrunc d∣m .snd
82+ k₂ = ∣-untrunc d∣n .fst
83+ k₂·d≡n = ∣-untrunc d∣n .snd
84+ p =
85+ (k₁ ∸ m / n · k₂) · d ≡⟨ ∸-distribʳ k₁ (m / n · k₂) d ⟩
86+ k₁ · d ∸ m / n · k₂ · d ≡⟨ sym $ cong (k₁ · d ∸_) (·-assoc (m / n) _ _) ⟩
87+ k₁ · d ∸ m / n · (k₂ · d) ≡⟨ cong₂ (λ l r → l ∸ m / n · r) k₁·d≡m k₂·d≡n ⟩
88+ m ∸ m / n · n ≡⟨ cong (m ∸_) (·-comm (m / n) n) ⟩
89+ m ∸ n · m / n ≡⟨ sym $ cong (_∸ n · m / n) (≡%+·/ n m) ⟩
90+ m % n + n · m / n ∸ n · m / n ≡⟨ +∸ (m % n) (n · m / n) ⟩
91+ m % n ∎
92+ in
93+ ∣ k₁ ∸ m / n · k₂ , p ∣₁
94+
95+ ∣%→∣ {m} {0 } {d} d∣m%n d∣n = d∣m%n
96+ ∣%→∣ {m} {n@(suc _)} {d} d∣m%n d∣n =
97+ let
98+ k₁ = ∣-untrunc d∣m%n .fst
99+ k₁·d≡m%n = ∣-untrunc d∣m%n .snd
100+ k₂ = ∣-untrunc d∣n .fst
101+ k₂·d≡n = ∣-untrunc d∣n .snd
102+ p =
103+ (k₁ + k₂ · m / n) · d ≡⟨ sym $ ·-distribʳ k₁ (k₂ · m / n) d ⟩
104+ k₁ · d + k₂ · m / n · d ≡⟨ cong (λ p → k₁ · d + p · d) (·-comm k₂ (m / n)) ⟩
105+ k₁ · d + m / n · k₂ · d ≡⟨ sym $ cong (k₁ · d +_) (·-assoc (m / n) k₂ d) ⟩
106+ k₁ · d + m / n · (k₂ · d) ≡⟨ cong₂ (λ l r → l + m / n · r) k₁·d≡m%n k₂·d≡n ⟩
107+ m % n + m / n · n ≡⟨ cong (m % n +_) (·-comm (m / n) n) ⟩
108+ m % n + n · m / n ≡⟨ ≡%+·/ n m ⟩
109+ m ∎
110+ in
111+ ∣ k₁ + k₂ · m / n , p ∣₁
89112
90113-- The inductive step of the Euclidean algorithm
91114stepGCD : isGCD (suc n) (m % suc n) d
92115 → isGCD m (suc n) d
93- fst (stepGCD ((d∣n , d∣m%n) , gr)) = PropTrunc.map2 lem₁ d∣n d∣m%n , d∣n
94- snd (stepGCD ((d∣n , d∣m%n) , gr)) d' (d'∣m , d'∣n) = gr d' (d'∣n , PropTrunc.map2 lem₂ d'∣n d'∣m)
95-
96- -- putting it all together using well-founded induction
97-
98- euclid< : ∀ m n → n < m → GCD m n
99- euclid< = WFI.induction <-wellfounded λ {
100- m rec zero p → m , zeroGCD m ;
101- m rec (suc n) p → let d , dGCD = rec (suc n) p (m % suc n) (n%sk<sk m n)
102- in d , stepGCD dGCD }
116+ stepGCD w =
117+ let
118+ g∣1+n = w .fst .fst
119+ g∣m%1+n = w .fst .snd
120+ d∣1+n→d∣m%1+n→d∣g = w .snd
121+ in
122+ (∣%→∣ g∣m%1+n g∣1+n , g∣1+n) ,
123+ λ d' (d'∣m , d'∣1+n) → d∣1+n→d∣m%1+n→d∣g d' (d'∣1+n , (∣→∣% d'∣m d'∣1+n))
124+
125+ -- putting it all together using an auxiliary variable to pass the termination checking
126+
127+ euclid-helper< : (m n f : ℕ) → (n < m) → (m ≤ f) → GCD m n
128+ euclid-helper< m n zero n<m m≤0 .fst = 0
129+ euclid-helper< m n zero n<m m≤0 .snd .fst .fst = ∣-refl $ sym $ ≤0→≡0 m≤0
130+ euclid-helper< m n zero n<m m≤0 .snd .fst .snd = ∣-refl $ sym $ ≤0→≡0 $
131+ <-weaken $ <≤-trans n<m m≤0
132+ euclid-helper< m n zero n<m m≤0 .snd .snd d' _ = ∣-zeroʳ d'
133+ euclid-helper< zero zero (suc _) _ _ .fst = 0
134+ euclid-helper< zero zero (suc _) _ _ .snd .fst .fst = ∣-refl refl
135+ euclid-helper< zero zero (suc _) _ _ .snd .fst .snd = ∣-refl refl
136+ euclid-helper< zero zero (suc _) _ _ .snd .snd d' _ = ∣-zeroʳ d'
137+ euclid-helper< zero (suc n) (suc _) _ _ .fst = suc n
138+ euclid-helper< zero (suc n) (suc _) _ _ .snd .fst .fst = ∣-zeroʳ (suc n)
139+ euclid-helper< zero (suc n) (suc _) _ _ .snd .fst .snd = ∣-refl refl
140+ euclid-helper< zero (suc n) (suc _) _ _ .snd .snd d' (_ , d'∣1+n) = d'∣1+n
141+ euclid-helper< (suc m) zero (suc _) _ _ .fst = suc m
142+ euclid-helper< (suc m) zero (suc _) _ _ .snd .fst .fst = ∣-refl refl
143+ euclid-helper< (suc m) zero (suc _) _ _ .snd .fst .snd = ∣-zeroʳ (suc m)
144+ euclid-helper< (suc m) zero (suc _) _ _ .snd .snd d' (d'∣1+m , _) = d'∣1+m
145+ euclid-helper< m@(suc _) n@(suc n-1) (suc f) n<m m≤1+f =
146+ let
147+ n≤f = predℕ-≤-predℕ $ <≤-trans n<m m≤1+f
148+ r = euclid-helper< n (m % n) f (%< n-1 m) n≤f
149+ in
150+ r .fst , stepGCD (r .snd)
103151
104152euclid : ∀ m n → GCD m n
105- euclid m n with n ≟ m
106- ... | lt p = euclid< m n p
107- ... | gt p = Σ.map-snd symGCD (euclid< n m p)
108- ... | eq p = m , divsGCD (∣-refl (sym p))
153+ euclid m zero = m , (∣-refl refl , ∣-zeroʳ m) , λ d' (d'∣m , _) → d'∣m
154+ euclid m n@(suc n-1) =
155+ let
156+ r = euclid-helper< n (m % n) n (%< n-1 m) ≤-refl
157+ in
158+ r .fst , stepGCD (r .snd)
159+
109160
110161isContrGCD : ∀ m n → isContr (GCD m n)
111162isContrGCD m n = euclid m n , isPropGCD _
@@ -165,3 +216,55 @@ isGCD-multʳ {m} {n} {d} k dGCD = gcd≡→isGCD (gcd-factorʳ m n k ∙ cong (_
165216
166217stDivIneqGCD : ¬ m ≡ 0 → ¬ m ∣ n → (g : GCD m n) → g .fst < m
167218stDivIneqGCD p q g = stDivIneq p q (g .snd .fst .fst) (g .snd .fst .snd)
219+
220+ -- Alternative definition of GCD using % from Cubical.Data.Fin and well-founded induction
221+
222+ private
223+ lem₁ : prediv d (suc n) → prediv d (m F.% suc n) → prediv d m
224+ lem₁ {d} {n} {m} (c₁ , p₁) (c₂ , p₂) = (q · c₁ + c₂) , p
225+ where r = m F.% suc n; q = n%k≡n[modk] m (suc n) .fst
226+ p = (q · c₁ + c₂) · d ≡⟨ sym (·-distribʳ (q · c₁) c₂ d) ⟩
227+ (q · c₁) · d + c₂ · d ≡⟨ cong (_+ c₂ · d) (sym (·-assoc q c₁ d)) ⟩
228+ q · (c₁ · d) + c₂ · d ≡[ i ]⟨ q · (p₁ i) + (p₂ i) ⟩
229+ q · (suc n) + r ≡⟨ n%k≡n[modk] m (suc n) .snd ⟩
230+ m ∎
231+
232+ lem₂ : prediv d (suc n) → prediv d m → prediv d (m F.% suc n)
233+ lem₂ {d} {n} {m} (c₁ , p₁) (c₂ , p₂) = c₂ ∸ q · c₁ , p
234+ where r = m F.% suc n; q = n%k≡n[modk] m (suc n) .fst
235+ p = (c₂ ∸ q · c₁) · d ≡⟨ ∸-distribʳ c₂ (q · c₁) d ⟩
236+ c₂ · d ∸ (q · c₁) · d ≡⟨ cong (c₂ · d ∸_) (sym (·-assoc q c₁ d)) ⟩
237+ c₂ · d ∸ q · (c₁ · d) ≡[ i ]⟨ p₂ i ∸ q · (p₁ i) ⟩
238+ m ∸ q · (suc n) ≡⟨ cong (_∸ q · (suc n)) (sym (n%k≡n[modk] m (suc n) .snd)) ⟩
239+ (q · (suc n) + r) ∸ q · (suc n) ≡⟨ cong (_∸ q · (suc n)) (+-comm (q · (suc n)) r) ⟩
240+ (r + q · (suc n)) ∸ q · (suc n) ≡⟨ ∸-cancelʳ r zero (q · (suc n)) ⟩
241+ r ∎
242+
243+ -- The inductive step of the Euclidean algorithm
244+ stepGCD' : isGCD (suc n) (m F.% suc n) d
245+ → isGCD m (suc n) d
246+ fst (stepGCD' ((d∣n , d∣m%n) , gr)) = PropTrunc.map2 lem₁ d∣n d∣m%n , d∣n
247+ snd (stepGCD' ((d∣n , d∣m%n) , gr)) d' (d'∣m , d'∣n) = gr d' (d'∣n , PropTrunc.map2 lem₂ d'∣n d'∣m)
248+
249+ -- putting it all together using well-founded induction
250+
251+ euclid'< : ∀ m n → n < m → GCD m n
252+ euclid'< = WFI.induction <-wellfounded λ {
253+ m rec zero p → m , zeroGCD m ;
254+ m rec (suc n) p → let d , dGCD = rec (suc n) p (m F.% suc n) (n%sk<sk m n)
255+ in d , stepGCD' dGCD }
256+
257+ euclid' : ∀ m n → GCD m n
258+ euclid' m n with n ≟ m
259+ ... | lt p = euclid'< m n p
260+ ... | gt p = Σ.map-snd symGCD (euclid'< n m p)
261+ ... | eq p = m , divsGCD (∣-refl (sym p))
262+
263+ gcd' : ℕ → ℕ → ℕ
264+ gcd' m n = euclid' m n .fst
265+
266+ euclid≡euclid' : ∀ m n → euclid m n ≡ euclid' m n
267+ euclid≡euclid' m n = isPropGCD (euclid m n) (euclid' m n)
268+
269+ gcd≡gcd' : ∀ m n → gcd m n ≡ gcd' m n
270+ gcd≡gcd' m n = cong fst (euclid≡euclid' m n)
0 commit comments