-
Notifications
You must be signed in to change notification settings - Fork 272
Continuing with generic GCD. #1392
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
08d1270
9f88991
e9d7b03
3315368
8bdb3ab
bf0e128
e308865
062054c
19226dc
1938536
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| ------------------------------------------------------------------------ | ||
| -- The Agda standard library | ||
| -- | ||
| -- Some properties of operations in CancellativeCommutativeSemiring. | ||
| ------------------------------------------------------------------------ | ||
|
|
||
| {-# OPTIONS --without-K --safe #-} | ||
|
|
||
| open import Algebra using (CancellativeCommutativeSemiring) | ||
| open import Algebra.Definitions using (AlmostRightCancellative) | ||
| open import Data.Sum.Base using (_⊎_; inj₁; inj₂) | ||
| open import Function using (_$_) | ||
| open import Relation.Binary using (Decidable) | ||
| import Relation.Binary.Reasoning.Setoid as EqReasoning | ||
| open import Relation.Nullary using (yes; no) | ||
| open import Relation.Nullary.Negation using (contradiction) | ||
|
|
||
| module Algebra.Properties.CancellativeCommutativeSemiring | ||
| {a ℓ} (R : CancellativeCommutativeSemiring a ℓ) | ||
| (open CancellativeCommutativeSemiring R) (_≟_ : Decidable _≈_) where | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
|
|
||
| open EqReasoning setoid | ||
|
|
||
| *-cancelʳ-nonZero : AlmostRightCancellative _≈_ 0# _*_ | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| -- ∀ {x} y z → ¬ x ≈ e → (y • x) ≈ (z • x) → y ≈ z | ||
| -- as * is commutative, left and right cancellation | ||
| -- are equivalent | ||
| *-cancelʳ-nonZero {x} y z x≉0 yx≈zx = *-cancelˡ-nonZero y z x≉0 $ begin | ||
| x * y ≈⟨ *-comm x y ⟩ | ||
| y * x ≈⟨ yx≈zx ⟩ | ||
| z * x ≈⟨ *-comm z x ⟩ | ||
| x * z ∎ | ||
|
|
||
| x*y≈0⇒x≈0⊎y≈0 : ∀ {x y} → x * y ≈ 0# → x ≈ 0# ⊎ y ≈ 0# | ||
| x*y≈0⇒x≈0⊎y≈0 {x} {y} xy≈0 with x ≟ 0# | y ≟ 0# | ||
| ... | yes x≈0 | _ = inj₁ x≈0 | ||
| ... | no _ | yes y≈0 = inj₂ y≈0 | ||
| ... | no x≉0 | no y≉0 = contradiction y≈0 y≉0 | ||
| where | ||
| xy≈x*0 = trans xy≈0 (sym (zeroʳ x)); y≈0 = *-cancelˡ-nonZero y 0# x≉0 xy≈x*0 | ||
|
|
||
| x≉0∧y≉0⇒xy≉0 : ∀ {x y} → x ≉ 0# → y ≉ 0# → x * y ≉ 0# | ||
| x≉0∧y≉0⇒xy≉0 x≉0 y≉0 xy≈0 with x*y≈0⇒x≈0⊎y≈0 xy≈0 | ||
| ... | inj₁ x≈0 = x≉0 x≈0 | ||
| ... | inj₂ y≈0 = y≉0 y≈0 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| ------------------------------------------------------------------------ | ||
| -- The Agda standard library | ||
| -- | ||
| -- Properties of the Greatest Common Divisor in CancellativeCommutativeSemiring. | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| ------------------------------------------------------------------------ | ||
|
|
||
| {-# OPTIONS --without-K --safe #-} | ||
|
|
||
| open import Algebra using (CancellativeCommutativeSemiring) | ||
| open import Data.Product using (_,_; proj₁; proj₂) | ||
| open import Data.Sum.Base using (_⊎_) | ||
| open import Relation.Binary using (Decidable) | ||
| import Relation.Binary.Reasoning.Setoid as EqReasoning | ||
| open import Relation.Nullary using (Dec; yes; no) | ||
|
|
||
| module Algebra.Properties.CancellativeCommutativeSemiring.GCD | ||
| {a ℓ} (R : CancellativeCommutativeSemiring a ℓ) | ||
| (open CancellativeCommutativeSemiring R) | ||
| (_≟_ : Decidable _≈_) | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| where | ||
|
|
||
| open import Algebra.Properties.Semiring.Divisibility semiring using (_∣0; 0∣x⇒x≈0) | ||
| open EqReasoning setoid | ||
| open import Algebra.Properties.Monoid.Divisibility *-monoid using | ||
| (∣-refl; ∣-trans; ∣-respˡ; ∣-respʳ; ∣∣-reflexive) | ||
| import Algebra.Properties.CommutativeSemigroup *-commutativeSemigroup as Of*CSemig | ||
| open import Algebra.Properties.CommutativeSemigroup.Divisibility | ||
| *-commutativeSemigroup using (x∣xy) | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| -- Re-exporting definition of GCD, primality, properties of GCD in semiring | ||
|
|
||
| open import Algebra.Divisibility _≈_ _*_ public | ||
| using (_∣_; _∣∣_) | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| open import Algebra.Primality _≈_ _*_ 0# 1# public | ||
| using (Coprime) | ||
| open import Algebra.Properties.Semiring.GCD semiring public | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| -- Properties of GCD | ||
|
|
||
| x≈0⊎y≉0⇒Coprime[x/gcd,y/gcd] : | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| ∀ {x y d} (isGCD : IsGCD x y d) → (x ≉ 0#) ⊎ (y ≉ 0#) → | ||
| let open IsGCD isGCD | ||
| in Coprime quot₁ quot₂ -- x/gcd(x,y) is coprime with y/gcd(x,y) | ||
| -- if any of x, y is nonzero | ||
|
|
||
| x≈0⊎y≉0⇒Coprime[x/gcd,y/gcd] {x} {y} {d} isGCD nz⊎nz {c} | ||
| (q₁ , q₁c≈quot₁) (q₂ , q₂c≈quot₂) = c∣1 | ||
| where | ||
| open IsGCD isGCD using (greatest; quot₁; quot₂; quot₁∙gcd≈x; quot₂∙gcd≈y) | ||
| d≉0 = x≉0⊎y≉0⇒gcd≉0 isGCD nz⊎nz | ||
| q₁*dc≈x = begin | ||
| q₁ * (d * c) ≈⟨ Of*CSemig.x∙yz≈xz∙y q₁ d c ⟩ | ||
| (q₁ * c) * d ≈⟨ *-congʳ q₁c≈quot₁ ⟩ | ||
| quot₁ * d ≈⟨ quot₁∙gcd≈x ⟩ | ||
| x ∎ | ||
|
|
||
| q₂*dc≈y = begin | ||
| q₂ * (d * c) ≈⟨ Of*CSemig.x∙yz≈xz∙y q₂ d c ⟩ | ||
| (q₂ * c) * d ≈⟨ *-congʳ q₂c≈quot₂ ⟩ | ||
| quot₂ * d ≈⟨ quot₂∙gcd≈y ⟩ | ||
| y ∎ | ||
|
|
||
| dc∣x = q₁ , q₁*dc≈x | ||
| dc∣y = q₂ , q₂*dc≈y | ||
| c∣1 = let | ||
| (q , q*dc≈d) = greatest dc∣x dc∣y | ||
| d*qc≈d*1 = begin | ||
| d * (q * c) ≈⟨ Of*CSemig.x∙yz≈y∙xz d q c ⟩ | ||
| q * (d * c) ≈⟨ q*dc≈d ⟩ | ||
| d ≈⟨ sym (*-identityʳ d) ⟩ | ||
| d * 1# ∎ | ||
|
|
||
| qc≈1 = *-cancelˡ-nonZero {d} (q * c) 1# d≉0 d*qc≈d*1 | ||
| in | ||
| q , qc≈1 | ||
|
|
||
| ------------------------------------------------------------------------------ | ||
| -- Recall that x ∣∣ y means that x and y are equal as association classes by | ||
| -- division. For example, for ℕ this means x ≡ y, | ||
| -- for ℤ this means (x ≡ y or x ≡ -y). | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
|
|
||
| GCD-unique : ∀ {x x' y y' d d'} → x ∣∣ x' → y ∣∣ y' → | ||
| IsGCD x y d → IsGCD x' y' d' → d ∣∣ d' | ||
| -- gcd-s for assoc-equal pairs are assoc-equal. | ||
| GCD-unique {x} {x'} {y} {y'} {d} {d'} (x∣x' , x'∣x) (y∣y' , y'∣y) isGCD isGCD' = | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| d∣d' , d'∣d | ||
| where | ||
| open IsGCD isGCD using () renaming (divides₁ to d∣x; divides₂ to d∣y) | ||
| open IsGCD isGCD' using () renaming (divides₁ to d'∣x'; divides₂ to d'∣y') | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| d∣x' = ∣-trans d∣x x∣x' | ||
| d∣y' = ∣-trans d∣y y∣y' | ||
| d∣d' = IsGCD.greatest isGCD' d∣x' d∣y' | ||
| d'∣x = ∣-trans d'∣x' x'∣x | ||
| d'∣y = ∣-trans d'∣y' y'∣y | ||
| d'∣d = IsGCD.greatest isGCD d'∣x d'∣y | ||
|
|
||
| ------------------------------------------------------------------------------ | ||
| -- gcd-distr is an important lemma of the gcd distributivity: | ||
| -- gcd (c*a) (c*b) assoc-equal c * (gcd a b). | ||
|
|
||
| gcd-distr : ∀ {a b c d d'} → IsGCD a b d → IsGCD (c * a) (c * b) d' → d' ∣∣ (c * d) | ||
|
MatthewDaggitt marked this conversation as resolved.
Outdated
|
||
| gcd-distr {a} {b} {c} {d} {d'} isGCD-a-b-d isGCD-ca-cb-d' = aux (c ≟ 0#) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This proof is pretty messy and unreadable. There must be a way of breaking it up into meaningful constituent parts, in the same way we have done so in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not so simple!
I do not find this lemma in textbooks. This is a folklore. A certain referee has pointed at its formulation. Can you provide a simpler proof? In Agda, it takes some code. Thus, the line of 65 characters is expressed in Agda in 240 characters (not counting trailing blanks). This is, for example, due to the following reasons. Please, consider the issue once more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's an example of one of your earlier proofs that I've just rewritten from: x≉0⊎y≉0⇒Coprime[x/gcd,y/gcd] : ∀ {x y d} → (x ≉ 0#) ⊎ (y ≉ 0#) →
((isGCDᶜ (q₁ , _) (q₂ , _) _) : IsGCD x y d) →
Coprime q₁ q₂
x≉0⊎y≉0⇒Coprime[x/gcd,y/gcd] {x} {y} {d} nz⊎nz
isGCD@(isGCDᶜ (quot₁ , quot₁∙gcd≈x) (quot₂ , quot₂∙gcd≈y) greatest)
{c} (q₁ , q₁c≈quot₁) (q₂ , q₂c≈quot₂) = c∣1
where
d≉0 = x≉0∨y≉0⇒gcd≉0 isGCD nz⊎nz
q₁*dc≈x = begin
q₁ * (d * c) ≈⟨ Of*CSemig.x∙yz≈xz∙y q₁ d c ⟩
(q₁ * c) * d ≈⟨ *-congʳ q₁c≈quot₁ ⟩
quot₁ * d ≈⟨ quot₁∙gcd≈x ⟩
x ∎
q₂*dc≈y = begin
q₂ * (d * c) ≈⟨ Of*CSemig.x∙yz≈xz∙y q₂ d c ⟩
(q₂ * c) * d ≈⟨ *-congʳ q₂c≈quot₂ ⟩
quot₂ * d ≈⟨ quot₂∙gcd≈y ⟩
y ∎
dc∣x = q₁ , q₁*dc≈x
dc∣y = q₂ , q₂*dc≈y
c∣1 = let
(q , q*dc≈d) = greatest dc∣x dc∣y
d*qc≈d*1 = begin
d * (q * c) ≈⟨ Of*CSemig.x∙yz≈y∙xz d q c ⟩
q * (d * c) ≈⟨ q*dc≈d ⟩
d ≈⟨ sym (*-identityʳ d) ⟩
d * 1# ∎
qc≈1 = *-cancelˡ-nonZero {d} (q * c) 1# d≉0 d*qc≈d*1
in
q , qc≈1to x∣y∧z∣x/y⇒x*z∣y : ∀ {x y z} → ((x/y , _) : x ∣ y) → z ∣ x/y → x * z ∣ y
x∣y∧z∣x/y⇒x*z∣y {x} {y} {z} (x/y , x/y*x≈y) (p , p*z≈x/y) = p , (begin
p * (x * z) ≈⟨ Of*CSemig.x∙yz≈xz∙y p x z ⟩
(p * z) * x ≈⟨ *-congʳ p*z≈x/y ⟩
x/y * x ≈⟨ x/y*x≈y ⟩
y ∎)
x*y∣x⇒y∣1 : ∀ {x y} → x ≉ 0# → x * y ∣ x → y ∣ 1#
x*y∣x⇒y∣1 {x} {y} x≉0 (q , q*xy≈x) = q , *-cancelˡ-nonZero (q * y) 1# x≉0 (begin
x * (q * y) ≈⟨ Of*CSemig.x∙yz≈y∙xz x q y ⟩
q * (x * y) ≈⟨ q*xy≈x ⟩
x ≈˘⟨ *-identityʳ x ⟩
x * 1# ∎)
x≉0⊎y≉0⇒Coprime[x/gcd,y/gcd]2 : ∀ {x y d} → x ≉ 0# ⊎ y ≉ 0# →
((isGCDᶜ (q₁ , _) (q₂ , _) _) : IsGCD x y d) →
Coprime q₁ q₂
x≉0⊎y≉0⇒Coprime[x/gcd,y/gcd]2 x≉0∨y≉0 gcd@(isGCDᶜ d∣x d∣y greatest) x/d∣z y/d∣z =
x*y∣x⇒y∣1 (x≉0∨y≉0⇒gcd≉0 gcd x≉0∨y≉0) (greatest
(x∣y∧z∣x/y⇒x*z∣y d∣x x/d∣z)
(x∣y∧z∣x/y⇒x*z∣y d∣y y/d∣z))Can you see the difference? There's significantly less code duplication and I've split out the sub-results into useful lemmas that can be re-used by other proofs. In contrast your proof is brittle as it relies on implementation details of the divisibility relation, hard to understand and therefore difficult to refactor. Please do the same for this proof.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an improvement for I have a minor note. Being a shorter proof does not mean being a more clear or understandable proof. For example the last three lines in your code are quite brain-twisting. This concerns the expressions in the last two lines. Putting
I add the lemma
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm afraid I do not understand what you mean.
Nowhere should the proof explicitly rely on the fact that divisibility is implemented as a pair. It should never directly construct or deconstruct a divisibility proof. Anything else breaks the abstraction boundary. Please fix.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More point: What do you write for the "abstract & encapsulated" style? ?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That second is not at all "abstract and encapsulated". I would call that imperative-style programming. It is an ugly version of the first code, with pretty much nothing going for it. Relying heavily on pattern-matching makes things representation-dependent. That is fine for implementing the base routines, and then gets problematic, eventually, for higher-level code. The code for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is it representation dependent? I think, not.
There is a concrete example of two lines. Can you write the function source for what you suggest?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Operating with ? |
||
| where | ||
| aux : Dec (c ≈ 0#) → d' ∣∣ (c * d) | ||
| aux (yes c≈0) = d'∣cd , cd∣d' -- A trivial case. The goal is reduced to 0 ∣∣ 0. | ||
| where | ||
| cd≈0 = trans (*-congʳ c≈0) (zeroˡ d) | ||
| d'∣cd = ∣-respʳ (sym cd≈0) (_∣0 d') -- the first part of the goal | ||
|
|
||
| ca≈0 = trans (*-congʳ c≈0) (zeroˡ a) | ||
| ca∣∣0 = ∣∣-reflexive ca≈0 | ||
| cb≈0 = trans (*-congʳ c≈0) (zeroˡ b) | ||
| cb∣∣0 = ∣∣-reflexive cb≈0 | ||
| d'∣∣0 = GCD-unique ca∣∣0 cb∣∣0 isGCD-ca-cb-d' (isGCD-0-x-x 0#) | ||
| d'≈0 = 0∣x⇒x≈0 (proj₂ d'∣∣0) | ||
| cd∣0 = _∣0 (c * d) | ||
| cd∣d' = ∣-respʳ (sym d'≈0) cd∣0 -- the second part of the goal | ||
|
|
||
| aux (no c≉0) = d'∣cd , cd∣d' -- general case | ||
| where | ||
| -- First derive cd ∣ d' from that cd divides both ca and cb. | ||
|
|
||
| open IsGCD isGCD-a-b-d using () | ||
| renaming (quot₁ to a'; quot₂ to b'; | ||
| quot₁∙gcd≈x to a'd≈a; quot₂∙gcd≈y to b'd≈b | ||
| ) | ||
| open IsGCD isGCD-ca-cb-d' using () renaming (divides₁ to d'∣ca; divides₂ to d'∣cb) | ||
| ca = c * a; cb = c * b; cd = c * d | ||
|
|
||
| cd∣ca = a' , a'*cd≈ca | ||
| where | ||
| a'*cd≈ca = begin | ||
| a' * (c * d) ≈⟨ Of*CSemig.x∙yz≈y∙xz a' c d ⟩ | ||
| c * (a' * d) ≈⟨ *-congˡ a'd≈a ⟩ | ||
| c * a ∎ | ||
|
|
||
| cd∣cb = b' , b'*cd≈cb | ||
| where | ||
| b'*cd≈cb = begin | ||
| b' * (c * d) ≈⟨ Of*CSemig.x∙yz≈y∙xz b' c d ⟩ | ||
| c * (b' * d) ≈⟨ *-congˡ b'd≈b ⟩ | ||
| c * b ∎ | ||
|
|
||
| cd∣d' = IsGCD.greatest isGCD-ca-cb-d' cd∣ca cd∣cb | ||
|
|
||
| -- Now, prove d' ∣ cd ---------------- | ||
|
|
||
| c∣ca = x∣xy c a; c∣cb = x∣xy c b -- hence xc ≈ gcd ca cb = d' for some x | ||
|
|
||
| c∣d' = IsGCD.greatest isGCD-ca-cb-d' c∣ca c∣cb | ||
| x = proj₁ c∣d' | ||
| xc≈d' = proj₂ c∣d' | ||
| xc∣ca = ∣-respˡ (sym xc≈d') d'∣ca | ||
| xc∣cb = ∣-respˡ (sym xc≈d') d'∣cb | ||
| y = proj₁ xc∣ca -- y*xc ≈ ca | ||
| z = proj₁ xc∣cb -- z*xc ≈ cb | ||
| ca≈c*yx = begin | ||
| c * a ≈⟨ sym (proj₂ xc∣ca) ⟩ | ||
| y * (x * c) ≈⟨ Of*CSemig.x∙yz≈z∙xy y x c ⟩ | ||
| c * (y * x) ∎ | ||
|
|
||
| cb≈c*zx = begin | ||
| c * b ≈⟨ sym (proj₂ xc∣cb) ⟩ | ||
| z * (x * c) ≈⟨ Of*CSemig.x∙yz≈z∙xy z x c ⟩ | ||
| c * (z * x) ∎ | ||
|
|
||
| yx≈a = *-cancelˡ-nonZero {c} (y * x) a c≉0 (sym ca≈c*yx) | ||
| zx≈b = *-cancelˡ-nonZero {c} (z * x) b c≉0 (sym cb≈c*zx) | ||
| x∣a = y , yx≈a | ||
| x∣b = z , zx≈b | ||
| x∣d = IsGCD.greatest isGCD-a-b-d x∣a x∣b | ||
| x' = proj₁ x∣d | ||
| x'x≈d = proj₂ x∣d | ||
| x'*cx≈cd = begin | ||
| x' * (c * x) ≈⟨ Of*CSemig.x∙yz≈y∙xz x' c x ⟩ | ||
| c * (x' * x) ≈⟨ *-congˡ x'x≈d ⟩ | ||
| c * d ∎ | ||
|
|
||
| cx∣cd = x' , x'*cx≈cd | ||
| cx≈d' = trans (*-comm c x) xc≈d' | ||
| d'∣cd = ∣-respˡ cx≈d' cx∣cd | ||
Uh oh!
There was an error while loading. Please reload this page.