Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ New modules
Algebra.Properties.CommutativeSemigroup.Divisibility
Algebra.Properties.Semiring.Divisibility
Algebra.Properties.Semiring.GCD
Algebra.Properties.CancellativeCommutativeSemiring
Algebra.Properties.CancellativeCommutativeSemiring.GCD
```

* Generic summation over algebraic structures
Expand Down Expand Up @@ -305,6 +307,7 @@ Other minor additions
RawNearSemiring c ℓ : Set (suc (c ⊔ ℓ))
RawLattice c ℓ : Set (suc (c ⊔ ℓ))
CancellativeCommutativeSemiring c ℓ : Set (suc (c ⊔ ℓ))
GCDSemiring c ℓ : Set (suc (c ⊔ ℓ))
```

* Added new definitions to `Algebra.Definitions`:
Expand Down
27 changes: 27 additions & 0 deletions src/Algebra/Bundles.agda
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,33 @@ record CancellativeCommutativeSemiring c ℓ : Set (suc (c ⊔ ℓ)) where
; _≉_
)

record GCDSemiring c ℓ : Set (suc (c ⊔ ℓ)) where
infixl 7 _*_
infixl 6 _+_
infix 4 _≈_
field
Carrier : Set c
_≈_ : Rel Carrier ℓ
_+_ : Op₂ Carrier
_*_ : Op₂ Carrier
0# : Carrier
1# : Carrier
isGCDSemiring : IsGCDSemiring _≈_ _+_ _*_ 0# 1#

open IsGCDSemiring isGCDSemiring public

cancellativeCommutativeSemiring : CancellativeCommutativeSemiring c ℓ
cancellativeCommutativeSemiring = record
{ Carrier = Carrier
; _≈_ = _≈_
; _+_ = _+_
; _*_ = _*_
; 0# = 0#
; 1# = 1#
; isCancellativeCommutativeSemiring = isCancellativeCommutativeSemiring
}


------------------------------------------------------------------------
-- Bundles with 2 binary operations, 1 unary operation & 2 elements
------------------------------------------------------------------------
Expand Down
19 changes: 16 additions & 3 deletions src/Algebra/Divisibility.agda
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
------------------------------------------------------------------------
-----------------------------------------------------------------------
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
-- The Agda standard library
--
-- Definition of divisibility
Expand All @@ -11,7 +11,7 @@
{-# OPTIONS --without-K --safe #-}

open import Algebra.Core
open import Data.Product using (∃; _×_; _,_)
open import Data.Product using (_×_; _,_; proj₁; proj₂; ∃)
open import Level using (_⊔_)
open import Relation.Binary
open import Relation.Nullary using (¬_)
Expand Down Expand Up @@ -70,12 +70,25 @@ x ∤∤ y = ¬ x ∣∣ y
-- Greatest common divisor (GCD)

record IsGCD (x y gcd : A) : Set (a ⊔ ℓ) where
constructor gcdᶜ
constructor isGCDᶜ
field
divides₁ : gcd ∣ x
divides₂ : gcd ∣ y
greatest : ∀ {z} → z ∣ x → z ∣ y → z ∣ gcd

quot₁ : A -- a complementory quotient x/gcd
quot₁ = proj₁ divides₁

quot₂ : A -- y/gcd
quot₂ = proj₁ divides₂

quot₁∙gcd≈x : (quot₁ ∙ gcd) ≈ x
quot₁∙gcd≈x = proj₂ divides₁

quot₂∙gcd≈y : (quot₂ ∙ gcd) ≈ y
quot₂∙gcd≈y = proj₂ divides₂


------------------------------------------------------------------------
-- Properties

Expand Down
45 changes: 45 additions & 0 deletions src/Algebra/Properties/CancellativeCommutativeSemiring.agda
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
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated

open EqReasoning setoid

*-cancelʳ-nonZero : AlmostRightCancellative _≈_ 0# _*_
Comment thread
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
183 changes: 183 additions & 0 deletions src/Algebra/Properties/CancellativeCommutativeSemiring/GCD.agda
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.
Comment thread
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 _≈_)
Comment thread
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 (_∣_; _∣∣_)
Comment thread
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] :
Comment thread
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).
Comment thread
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' =
Comment thread
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')
Comment thread
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)
Comment thread
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#)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 Data.Nat.GCD!

@mechvel mechvel Jan 5, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not so simple!
I do not know of how to simplify it any essentially.
Data.Nat.GCD would not help. Because it uses the properties of division with remainder (_/_ relies on division with remainder), on the properties of _/_ on .
And generic CancellativeCommutativeSemiring does not possess such an operation with such properties.
Among R : CancellativeCommutativeSemiring there are some domains R for which there is not possible a division operation with the properties similar to ones of Nat.DivMod... In particular there is not possible in general to solve
x ∣? y in R, while for it is solved via divMod.

gcd-distr is a subtle and a particular lemma. It nicety is in that its proof does not use anything besides the three
axioms of GCD (and the properties of generic _∣_).
The first impression is that gcd-distr cannot be derived only from these laws. But it occurs that the goal is achieved by combining these three laws repeatedly, and in a certain way.

I do not find this lemma in textbooks. This is a folklore. A certain referee has pointed at its formulation.
Then, I searched for the question at a mathematical forum, and found there a proof sketch.
The true author of the lemma is not known. It is almost the same as the generic Euclid's lemma (which is also
a folklore).
Coq does have its proof.
But I prefer to take things from literature rather than to read programs: to read an in-formal proof, to add details, and to convert it to Agda.
I have rewritten this proof in detail, with generalizing it from IntegralDomain to CancellativeCommutativeSemiring,
and programmed it.
Hear is this short and clear in-formal proof:

proof2.lagda.zip

Can you provide a simpler proof?

In Agda, it takes some code. Thus, the line

If c = 0 then  gcd ac bc  ∥  gcd 0 0  ∣∣  0  ∣∣  c ∙ (gcd a b).

of 65 characters is expressed in Agda in 240 characters (not counting trailing blanks).

This is, for example, due to the following reasons.
a) Agda needs all details.
b) For readability, these assignments with '=' need to be one per line.
c) In comparison to Nat, is replaced in many cases with ∣∣, which is expanded to the expressions
containing two pairs of values, the two equalities to be proved, the two factor values to be introduced.

Please, consider the issue once more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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≈1

to

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.

@mechvel mechvel Jan 30, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an improvement for x≉0⊎y≉0⇒Coprime[x/gcd,y/gcd] . Thank you.
I must have introduced this x∣y∧z∣x/y⇒x*z∣y.
Also I did not know of the possibility to set patterns like ((foo , _) : x ∣ y) →
to signatures.
x*y∣x⇒y∣1 is also useful.
Now, I
a) rename x∣y∧z∣x/y⇒x*z∣y to x∣y∧z∣x/y⇒xz∣y
(in mathematics, they often write xy for x * y, and I suggest to do this in names
- - when it is clear that this means x * y or x ∙ y),
b) rename x*y∣x⇒y∣1 to xy∣x⇒y∣1,
c) move x∣y∧z∣x/y⇒xz∣y to Algebra.Properties.CommutativeSemigroup.Divisibility,
because it is more generic, it is for CommutativeSemigroup and its _∙_.
d) move xy∣x⇒y∣1 to Algebra.Properties.CancellativeCommutativeSemiring,
because its usage is not only for the GCD items.

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
foo1 = x∣y∧z∣x/y⇒x*z∣y d∣x x/d∣z, who divides who according to foo1 ?
Putting foo2 = x∣y∧z∣x/y⇒x*z∣y d∣y y/d∣z, does it return the same value as foo1 ?
How many minutes with take of the reader to find this out?
Personally I can find it out, but have not done this, so far ...
Do we need to frighten readers?
Probably it worth to write where <this>|<that> = x∣y∧z∣x/y⇒x*z∣y d∣x x/d∣z
with setting appropriate names for <this> and <that>.

gcd-distr : Decidable _≈_ → ∀ {a b c d d'} → IsGCD a b d →                        
                  IsGCD (c * a) (c * b) d' → d' ∣∣ (c * d)

Please do the same for this proof.

I add the lemma x∣y⇒zx∣zy to Algebra.Properties.CommutativeSemigroup.Divisibility
and use it here three times.
I change where to let in a part of this proof in order to use patterns like
(x , xc≈d') = IsGCD.greatest ... and to save extracting values from it by the two projections.
Now it is somewhat simper and shorter. I wonder of what else can be done.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting foo1 = x∣y∧z∣x/y⇒xz∣y d∣x x/d∣z, who divides who according to foo1 ?
Putting foo2 = x∣y∧z∣x/y⇒x
z∣y d∣y y/d∣z, does it return the same value as foo1 ?

I'm afraid I do not understand what you mean.

Now it is somewhat simper and shorter. I wonder of what else can be done.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The whole subject is evident, and has nothing to do with mathematics.
  2. If we define divisibility as a record Divides with constructor divides, the fields quotient and
    equality, then (divides q eq) : Divides a b is an abstract enough thing.
  3. Probably Matthew is right in that gcd-distr can be written in a better way.
  4. Even the current version of divisibility, IsGCD, and gcd-distr is still much better than the whole design in lib-1.5 for divisibility, GCD, Rational and such - even if the latter's are programmed very "encapsulatedly".
    Generally it is desirable to distinguish intuitively important things from not so important.

@mechvel mechvel Feb 23, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More point:
when a language relies heavily on pattern matching, this drives it from the abstraction of the kind you people are so eager of.
For example, I expect to find in standard library programs similar to

f (_ :: (_ , y) :: ps) = y + ( f  ps)
f  _ = 0

What do you write for the "abstract & encapsulated" style?
Is it

f ps  = case null? ps
           of \
           { (yes _) -> 0 
           ; (no notNull-ps) -> 
                   case null? (tail ps notNull-ps)
                   of \
                    { (yes _) -> 0
                    ; (no notNull-tlPs) -> proj-2  (head (tail ps notNull-ps) notNull-tlPs) + (f ...)
                    } 
              }

?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 f clearly is a kind of fold that works on even length lists. The "abstract and encapsulated" way of doing this would be to provide such a fold abstraction, and then instantiate it with 0 and \x acc -> proj-2 x + acc.

@mechvel mechvel Feb 23, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That second is not at all "abstract and encapsulated".

Is it representation dependent? I think, not.
It uses only 1) null? to decide of emptiness,
2) head and tail for a nonempty a list, these actually constitute a definition of what is a list,
3) second projection for a direct product.
Only basic mathematical notions, the representation details are not visible.
"Encapsulated style" or not - anyway, what is wrong in it (except its ugliness) ?

to provide such a fold abstraction, and then instantiate it with 0 and \x acc -> proj-2 x + acc.

There is a concrete example of two lines. Can you write the function source for what you suggest?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and then gets problematic, eventually, for higher-level code.

Operating with gcd , IsGCD is higher-level.
Does this mean that one needs to avoid writing things like

let (mkIsGCD g g|a g|b greatest) = gcd a b in ...

?

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
3 changes: 3 additions & 0 deletions src/Algebra/Properties/Monoid/Divisibility.agda
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ open import Algebra.Properties.Semigroup.Divisibility semigroup public
∣-reflexive : _≈_ ⇒ _∣_
∣-reflexive = Div.∣-reflexive trans identityˡ

∣∣-reflexive : _≈_ ⇒ _∣∣_
∣∣-reflexive x≈y = ∣-reflexive x≈y , ∣-reflexive (sym x≈y)
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated

∣-isPreorder : IsPreorder _≈_ _∣_
∣-isPreorder = record
{ isEquivalence = isEquivalence
Expand Down
31 changes: 26 additions & 5 deletions src/Algebra/Properties/Semiring/GCD.agda
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,45 @@
{-# OPTIONS --without-K --safe #-}

open import Algebra using (Semiring)
import Algebra.Properties.Monoid.Divisibility as MonoidDiv
open import Data.Product using (_,_)
import Algebra.Primality as Primality
open import Data.Sum.Base using (_⊎_; inj₁; inj₂)

module Algebra.Properties.Semiring.GCD {a ℓ} (R : Semiring a ℓ) where

open Semiring R
open Primality _≈_ _*_ 0# 1# using (Coprime)
-- open import Algebra.Properties.Monoid.Divisibility *-monoid using (∣-refl)
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated

open import Algebra.Properties.Semiring.Divisibility R

------------------------------------------------------------------------
-- Re-exporting definition of GCD

open import Algebra.Divisibility _≈_ _*_ public
using (IsGCD; gcdᶜ)
using (IsGCD; isGCDᶜ)

------------------------------------------------------------------------
-- Properties of GCD

isGCD-0-x-x : ∀ x → IsGCD 0# x x
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
isGCD-0-x-x x = isGCDᶜ (_∣0 x) ∣-refl (λ _ y∣x → y∣x)

isGCD-x-0-x : ∀ x → IsGCD x 0# x
isGCD-x-0-x x = isGCDᶜ ∣-refl (_∣0 x) (λ y∣x _ → y∣x)

x≉0⊎y≉0⇒gcd≉0 : ∀ {x y d} → IsGCD x y d → x ≉ 0# ⊎ y ≉ 0# → d ≉ 0#
x≉0⊎y≉0⇒gcd≉0 (gcdᶜ d∣x _ _) (inj₁ x≉0) = x∣y∧y≉0⇒x≉0 d∣x x≉0
x≉0⊎y≉0⇒gcd≉0 (gcdᶜ _ d∣y _) (inj₂ y≉0) = x∣y∧y≉0⇒x≉0 d∣y y≉0
x≉0⊎y≉0⇒gcd≉0 (isGCDᶜ d∣x _ _) (inj₁ x≉0) = x∣y∧y≉0⇒x≉0 d∣x x≉0
x≉0⊎y≉0⇒gcd≉0 (isGCDᶜ _ d∣y _) (inj₂ y≉0) = x∣y∧y≉0⇒x≉0 d∣y y≉0

x≈0∧y≈0⇒gcd≈0 : ∀ {x y d} → IsGCD x y d → x ≈ 0# → y ≈ 0# → d ≈ 0#
x≈0∧y≈0⇒gcd≈0 (isGCDᶜ _ _ greatest) x≈0 y≈0 = 0∣x⇒x≈0 0∣d
where
0∣x = ∣-respʳ (sym x≈0) (0# ∣0)
0∣y = ∣-respʳ (sym y≈0) (0# ∣0)
0∣d = greatest {0#} 0∣x 0∣y

coprime⇒gcd∣1 : ∀ {x y d} → IsGCD x y d → Coprime x y → d ∣ 1#
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
-- (gcd x y) is invertible for (coprime x y)
coprime⇒gcd∣1 isGCD coprime-x-y = coprime-x-y divides₁ divides₂
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
where
open IsGCD isGCD
Comment thread
MatthewDaggitt marked this conversation as resolved.
Outdated
Loading