Andrew Sneap - 27th April 2021 Updated July 2022 In this file I define common divisors, and HCF's, along with a proof that the Euclidean Algorithm produces HCF's. \begin{code} {-# OPTIONS --safe --without-K #-} open import MLTT.Spartan renaming (_+_ to _∔_) open import Naturals.Addition open import Naturals.Division open import Naturals.Multiplication open import Naturals.Order open import Naturals.Properties open import Notation.Order open import UF.DiscreteAndSeparated open import UF.FunExt open import UF.Subsingletons open import UF.Subsingletons-FunExt module Naturals.HCF where \end{code} A common divisor d of x and y is a Natural Number which divides x and y, and clearly is a proposition. \begin{code} is-common-divisor : (d x y : ℕ) → 𝓤₀ ̇ is-common-divisor d x y = (d ∣ x) × (d ∣ y) is-common-divisor-is-prop : (d x y : ℕ) → is-prop (is-common-divisor (succ d) x y) is-common-divisor-is-prop d x y = ×-is-prop (_∣_-is-prop d x) (_∣_-is-prop d y) \end{code} The highest common divisor of x and y is the common divisor of x and y which is greater than all other common divisors. One way to formulate the type of the hcf h of x and y is to say that any other common factor is a divisor of the highest common factor. \begin{code} is-hcf : (h x y : ℕ) → 𝓤₀ ̇ is-hcf h x y = (is-common-divisor h x y) × ((d : ℕ) → is-common-divisor d x y → d ∣ h) \end{code} Of course, any we can retrieve from the cartesian product that the hcf is a common divisor. \begin{code} is-hcf-gives-is-common-divisor : (h x y : ℕ) → is-hcf h x y → is-common-divisor h x y is-hcf-gives-is-common-divisor h x y (a , p) = a \end{code} The statement "succ h is the highest common factor of x and y" is a proposition. In order to prove this, function extensionality is required, because the second projection of the cartesian product is a function. With function extensionality, proof that this statement is a proposition follows from the proof that (is-common-divisor d x y) is a proposition. \begin{code} is-hcf-is-prop : Fun-Ext → (h x y : ℕ) → is-prop (is-hcf (succ h) x y) is-hcf-is-prop fe h x y p q = ×-is-prop (is-common-divisor-is-prop h x y) II p q where I : (d : ℕ) → is-common-divisor d x y → is-prop (d ∣ succ h) I 0 i x = 𝟘-elim (zero-does-not-divide-positive h x) I (succ d) i = d ∣ (succ h) -is-prop II : is-prop ((d : ℕ) → is-common-divisor d x y → d ∣ succ h) II p' q' = Π₂-is-prop fe I p' q' \end{code} Of course, hcf is commutative, which is easily proved by re-ordering projections, and other properties of hcf are simple corollaries of the definition. \begin{code} hcf-comm : (x y h : ℕ) → is-hcf h x y → is-hcf h y x hcf-comm x y h ((h∣x , h∣y) , f) = (h∣y , h∣x) , γ where γ : (d : ℕ) → is-common-divisor d y x → d ∣ h γ d (d∣y , d∣x) = f d (d∣x , d∣y) hcf-comm' : (x y : ℕ) → Σ h ꞉ ℕ , is-hcf h x y → Σ h ꞉ ℕ , is-hcf h y x hcf-comm' x y (h , is-hcf) = h , (hcf-comm x y h is-hcf) hcf-one-left : {x : ℕ} → is-hcf 1 1 x hcf-one-left {x} = (∣-refl , 1-divides-all x) , γ where γ : (d : ℕ) → is-common-divisor d 1 x → d ∣ 1 γ d (d-divides-1 , _) = d-divides-1 hcf-one-right : {x : ℕ} → is-hcf 1 x 1 hcf-one-right {x} = hcf-comm 1 x 1 hcf-one-left hcf-refl : {x : ℕ} → is-hcf x x x hcf-refl {x} = (∣-refl , ∣-refl) , γ where γ : (d : ℕ) → is-common-divisor d x x → d ∣ x γ d (d-divides-x , _) = d-divides-x hcf-zero-left : {x : ℕ} → is-hcf x 0 x hcf-zero-left {x} = (everything-divides-zero , ∣-refl) , γ where γ : (d : ℕ) → is-common-divisor d 0 x → d ∣ x γ d (_ , d-divides-0) = d-divides-0 hcf-zero-right : {x : ℕ} → is-hcf x x 0 hcf-zero-right {x} = hcf-comm 0 x x hcf-zero-left \end{code} With an eye towards implement Euclid's algorithm to compute the highest common factor, we now prove two lemmas; each direction of the following proof: If x = q * y + r, then is-hcf h x y ↔ is-hcf y r. For Euclid's algorithm, we only need the right-to-left implication, but both are proved for completeness. The general idea of the right-to-left implication is as follows: x = q * y + r, h | y and h | r, with h = hcf(y , r). Now, clearly h | x since h | (q * y + r), and h | y by assumption, so h is a common factor of x and y. To show that h is the highest common factor, assume that d | x, d | y, and further that d * u = x , d * v = y for some u , v. If we can show that d | y, and d | r, then d | h since is-hcf h y r. First, d | y by assumption. Now, d * u = q * (d * v) + r, so by the factor-of-sum-consequence, d | r, and we are done. \begin{code} euclids-algorithm-lemma : (x y q r h : ℕ) → x = q * y + r → is-hcf h x y → is-hcf h y r euclids-algorithm-lemma x y q r h e (((a , e₀) , b , e₁) , f) = I , II where I : is-common-divisor h y r I = (b , e₁) , factor-of-sum-consequence h a (q * b) r i where i : h * a = h * (q * b) + r i = h * a =⟨ e₀ ⟩ x =⟨ e ⟩ q * y + r =⟨ ap (λ - → q * - + r) (e₁ ⁻¹) ⟩ q * (h * b) + r =⟨ ap (_+ r) (mult-associativity q h b ⁻¹) ⟩ q * h * b + r =⟨ ap (λ - → - * b + r) (mult-commutativity q h) ⟩ h * q * b + r =⟨ ap (_+ r) (mult-associativity h q b) ⟩ h * (q * b) + r ∎ II : (d : ℕ) → is-common-divisor d y r → d ∣ h II d ((u , e₁) , v , e₂) = f d ((q * u + v , i) , u , e₁) where i : d * (q * u + v) = x i = d * (q * u + v) =⟨ distributivity-mult-over-addition d (q * u) v ⟩ d * (q * u) + d * v =⟨ ap (d * (q * u) +_) e₂ ⟩ d * (q * u) + r =⟨ ap (_+ r) (mult-associativity d q u ⁻¹) ⟩ d * q * u + r =⟨ ap (λ - → - * u + r) (mult-commutativity d q) ⟩ q * d * u + r =⟨ ap (_+ r) (mult-associativity q d u) ⟩ q * (d * u) + r =⟨ ap (λ - → q * - + r) e₁ ⟩ q * y + r =⟨ e ⁻¹ ⟩ x ∎ euclids-algorithm-lemma' : (x y q r h : ℕ) → x = q * y + r → is-hcf h y r → is-hcf h x y euclids-algorithm-lemma' x y q r h e (((a , e₀) , b , e₁) , f) = I , II where I : is-common-divisor h x y I = (q * a + b , i) , (a , e₀) where i : h * (q * a + b) = x i = h * (q * a + b) =⟨ distributivity-mult-over-addition h (q * a) b ⟩ h * (q * a) + h * b =⟨ ap (h * (q * a) +_) e₁ ⟩ h * (q * a) + r =⟨ ap (_+ r) (mult-associativity h q a ⁻¹) ⟩ h * q * a + r =⟨ ap (λ - → - * a + r) (mult-commutativity h q) ⟩ q * h * a + r =⟨ ap (_+ r) (mult-associativity q h a) ⟩ q * (h * a) + r =⟨ ap (λ - → q * - + r) e₀ ⟩ q * y + r =⟨ e ⁻¹ ⟩ x ∎ II : (d : ℕ) → is-common-divisor d x y → d ∣ h II d ((u , e₂) , v , e₃) = f d ((v , e₃) , ii) where i : d * u = d * (q * v) + r i = d * u =⟨ e₂ ⟩ x =⟨ e ⟩ q * y + r =⟨ ap (λ - → q * - + r) (e₃ ⁻¹) ⟩ q * (d * v) + r =⟨ ap (_+ r) (mult-associativity q d v ⁻¹) ⟩ q * d * v + r =⟨ ap (λ - → - * v + r) (mult-commutativity q d) ⟩ d * q * v + r =⟨ ap (_+ r) (mult-associativity d q v) ⟩ d * (q * v) + r ∎ ii : d ∣ r ii = factor-of-sum-consequence d u (q * v) r i \end{code} Now we have the function which computes the highest common factor for any two natural numbers x and y. This function uses course-of-values induction in order to satisfy the Agda termination checker. The step function includes an induction, which says the following: If for any number x, we can find a number r with r < x, and for any number k there exists a highest common factor of r and k, then for any y there exists a highest common factor of x and y. \begin{code} HCF : (x y : ℕ) → Σ h ꞉ ℕ , is-hcf h x y HCF = course-of-values-induction (λ x → (y : ℕ) → Σ h ꞉ ℕ , is-hcf h x y) step where step : (x : ℕ) → ((r : ℕ) → r < x → (y : ℕ) → Σ h ꞉ ℕ , is-hcf h r y) → (y : ℕ) → Σ h ꞉ ℕ , is-hcf h x y step 0 IH y = y , (everything-divides-zero , ∣-refl) , γ where γ : (d : ℕ) → is-common-divisor d 0 y → d ∣ y γ d (a , b) = b step (succ x) IH y = I (division y x) where I : Σ q ꞉ ℕ , Σ r ꞉ ℕ , (y = q * succ x + r) × (r < succ x) → Σ h ꞉ ℕ , is-hcf h (succ x) y I (q , r , e₀ , l) = II (IH r l (succ x)) where II : Σ h ꞉ ℕ , is-hcf h r (succ x) → Σ h ꞉ ℕ , is-hcf h (succ x) y II (h , h-is-hcf) = h , hcf-comm y (succ x) h ii where i : is-hcf h (succ x) r i = hcf-comm r (succ x) h h-is-hcf ii : is-hcf h y (succ x) ii = euclids-algorithm-lemma' y (succ x) q r h e₀ i hcf : (x y : ℕ) → ℕ hcf x y = pr₁ (HCF x y) hcf-is-HCF : (x y : ℕ) → is-hcf (hcf x y) x y hcf-is-HCF x y = pr₂ (HCF x y) \end{code} Two numbers being coprime is also a proposition, as a simple consequence of hcf being a proposition for all values of h. Two numbers are coprime in the special case that the hcf is 1. \begin{code} coprime' : ℕ → ℕ → 𝓤₀ ̇ coprime' x y = hcf x y = 1 coprime'-is-prop : (x y : ℕ) → is-prop (coprime' x y) coprime'-is-prop _ _ = ℕ-is-set coprime : (a b : ℕ) → 𝓤₀ ̇ coprime a b = is-hcf 1 a b coprime-is-prop : Fun-Ext → (a b : ℕ) → is-prop (coprime a b) coprime-is-prop fe a b = is-hcf-is-prop fe 0 a b coprime'-to-coprime : (x y : ℕ) → coprime' x y → coprime x y coprime'-to-coprime x y p = transport (λ - → is-hcf - x y) p (hcf-is-HCF x y) coprime-0-1 : coprime 0 1 coprime-0-1 = (1-divides-all 0 , 1-divides-all 1) , γ where γ : (d : ℕ) → is-common-divisor d 0 1 → d ∣ 1 γ d (_ , d-divides-one) = d-divides-one divbyhcf' : (a b : ℕ) → Σ h ꞉ ℕ , Σ x ꞉ ℕ , Σ y ꞉ ℕ , ((h * x = a) × (h * y = b)) × coprime x y divbyhcf' 0 b = b , 0 , 1 , (refl , refl) , coprime-0-1 divbyhcf' (succ a) b = γ' (HCF (succ a) b) where γ' : Σ h ꞉ ℕ , is-hcf h (succ a) b → Σ h ꞉ ℕ , Σ x ꞉ ℕ , Σ y ꞉ ℕ , ((h * x = succ a) × (h * y = b)) × coprime x y γ' (0 , (p , _) , τ) = 𝟘-elim (zero-does-not-divide-positive a p) γ' (succ h , ((x , α) , (y , β)) , τ) = succ h , x , y , (α , β) , γ where γ₁ : is-common-divisor 1 x y γ₁ = 1-divides-all x , 1-divides-all y γ₂ : (d : ℕ) → is-common-divisor d x y → d ∣ 1 γ₂ d ((k , δ) , (l , ψ)) = division-refl-right-factor h d II where I : (k x a : ℕ) → d * k = x → succ h * x = a → succ h * d ∣ a I k x a e₁ e₂ = k , (succ h * d * k =⟨ mult-associativity (succ h) d k ⟩ succ h * (d * k) =⟨ ap (succ h *_) e₁ ⟩ succ h * x =⟨ e₂ ⟩ a ∎) II : (succ h * d) ∣ succ h II = τ (succ h * d) (I k x (succ a) δ α , I l y b ψ β) γ : coprime x y γ = γ₁ , γ₂ divbyhcf : (a b : ℕ) → Σ h ꞉ ℕ , Σ x ꞉ ℕ , Σ y ꞉ ℕ , ((h * x = a) × (h * y = b)) × coprime x y divbyhcf 0 b = b , 0 , 1 , I , II , III where I : (b * 0 = zero) × (b * 1 = b) I = refl , refl II : (1 ∣ 0) × (1 ∣ 1) II = everything-divides-zero , 1-divides-all 1 III : (d : ℕ) → is-common-divisor d 0 1 → d ∣ 1 III d (_ , d-divides-one) = d-divides-one divbyhcf (succ a) b = I (HCF (succ a) b) where I : Σ c ꞉ ℕ , is-hcf c (succ a) b → Σ h ꞉ ℕ , Σ x ꞉ ℕ , Σ y ꞉ ℕ , ((h * x = succ a) × (h * y = b)) × coprime x y I (0 , ((x , xₚ) , y , yₚ) , τ) = 𝟘-elim (positive-not-zero a II) where II : succ a = 0 II = succ a =⟨ xₚ ⁻¹ ⟩ 0 * x =⟨ mult-commutativity zero x ⟩ 0 ∎ I (succ h , ((x , xₚ) , y , yₚ) , τ) = succ h , x , y , (xₚ , yₚ) , goal where II : (f' : ℕ) → is-common-divisor f' x y → f' ∣ 1 II f' ((α , αₚ) , β , βₚ) = III (τ (succ h * f') ((α , αₚ') , β , βₚ')) where αₚ' : succ h * f' * α = succ a αₚ' = succ h * f' * α =⟨ mult-associativity (succ h) f' α ⟩ succ h * (f' * α) =⟨ ap (succ h *_) αₚ ⟩ succ h * x =⟨ xₚ ⟩ succ a ∎ βₚ' : succ h * f' * β = b βₚ' = succ h * f' * β =⟨ mult-associativity (succ h) f' β ⟩ succ h * (f' * β) =⟨ ap (succ h *_) βₚ ⟩ succ h * y =⟨ yₚ ⟩ b ∎ III : (succ h) * f' ∣ (succ h) → f' ∣ 1 III (δ , δₚ) = 1 , left-factor-one f' δ γ where e : succ h * (f' * δ) = succ h * 1 e = succ h * (f' * δ) =⟨ mult-associativity (succ h) f' δ ⁻¹ ⟩ succ h * f' * δ =⟨ δₚ ⟩ succ h ∎ γ : f' * δ = 1 γ = mult-left-cancellable (f' * δ) 1 h e goal : coprime x y goal = (1-divides-all x , 1-divides-all y) , II hcf-unique : (a b : ℕ) → ((h , p) : Σ h ꞉ ℕ , is-hcf h a b) → ((h' , p') : Σ h' ꞉ ℕ , is-hcf h' a b) → h = h' hcf-unique a b (h , h-icd , f) (h' , h'-icd , f') = ∣-anti h h' I II where I : h ∣ h' I = f' h h-icd II : h' ∣ h II = f h' h'-icd coprime-to-coprime' : (x y : ℕ) → coprime x y → coprime' x y coprime-to-coprime' x y p = γ where I : is-hcf (hcf x y) x y I = hcf-is-HCF x y γ : hcf x y = 1 γ = hcf-unique x y (hcf x y , I) (1 , p) \end{code} The statement "x and y have a highest-common-factor" is also a proposition. Again, function extensionality is required. To prove that the hcf is unique, we assume there are two different hcf's. But by the definition of is-hcf, all common factors are factors of the hcf, and both hcf's are common factors. Two numbers which are factors of each other are equal by the anti-symmetric property of division. \begin{code} has-hcf : (x y : ℕ) → 𝓤₀ ̇ has-hcf x y = Σ d ꞉ ℕ , is-hcf (succ d) x y has-hcf-is-prop : Fun-Ext → (x y : ℕ) → is-prop (has-hcf x y) has-hcf-is-prop fe x y (h₁ , h₁-hcf) (h₂ , h₂-hcf) = to-subtype-= γ₁ γ₂ where γ₁ : (d : ℕ) → is-prop (is-hcf (succ d) x y) γ₁ d = is-hcf-is-prop fe d x y I : succ h₁ = succ h₂ I = hcf-unique x y (succ h₁ , h₁-hcf) (succ h₂ , h₂-hcf) γ₂ : h₁ = h₂ γ₂ = succ-lc I \end{code}