[Agda] GCD in future standard library

mechvel at scico.botik.ru mechvel at scico.botik.ru
Tue Aug 6 15:42:41 CEST 2019


Dear standard library developers,

this is on developing the algebra part of the standard library.

In the _master_ library branch, we come now to treating GCD for the new 
Bin representation.
And this touches the question of possible generic GCD notion.
The two approaches are possible, and I need to agree on this point.

First, consider the GCD notion itself, without relating it to the 
computation method.

In the current _master_ library there are the three very similar numeric 
domains:

                         ℕ, Bin, ℤ.

ℕ is isomorphic to Bin, and most methods for ℤ are reduced to those for 
ℕ in a simple way, without loss of efficiency.
I think, also  ℤb  needs to join -- binary represented integers.

For most algorithms, we cannot use the toℕ/fromℕ isomorphism to compute 
things on Bin (for example, divMod, gcd).
Because this isomorphism is expensive to compute relatively to the 
method itself (even by using certain built-in primitives).

The approach of lib-1.1 is as follows.
* There are declared the two _notions_ of GCD:  for ℕ and for ℤ,
   and there needs to appear the third GCD notion -- for Bin.
* There is no provision for the GCD notion for other domains, for 
example, for polynomials over a domain with gcd.

This design does not look natural.

We need now to choice between the two approaches.


I. To continue the lib-1.1 design:
    to declare one more GCD notion -- Bin.GCD, and to compute Bin.gcd by 
using  Bin.divMod.


II. A generic approach, which I suggest.

1) To add to  Algebra.agda  the declaration of a cancellative semiring:

record CancellativeSemiring α ℓ :  Set _
   where
   field  CommutativeSemiring : CommutativeSemiring α ℓ
   ...
   field  _≟_       : Decidable _≈_
          lnzCancel : LeftNZCancellative

LeftNZCancellative means the Cancellative property already defined in 
the _master_ library, only it adds the
condition x ≉ 0# for the element being cancelled.

2) The GCD notion is defined in the module parameterized by  R : 
CancellativeSemiring _ _:

   record GCD (a b : Carrier) :  Set _   -- a result of gcd
     where
     constructor gcd′
     field  proper   : Carrier           -- proper gcd value
            divides₁ : proper ∣ a
            divides₂ : proper ∣ b
            greatest : ∀ {d} → (d ∣ a) → (d ∣ b) → (d ∣ proper)

It is similar to the definitions existing in lib-1.1, only it is 
generic.
Also in this module are declared the general notions of
                                                      IsPrime, Coprime.
All this is expressed very simply.

3) The domains ℕ, ℤ, Bin are already actually provided in the _master_ 
lib with the instances of CancellativeSemiring,
    because the cancellation law by nonzero is already implemented there 
for these domains.
Hence the above GCD declaration becomes type-checked and ready to work, 
and it will replace the three special GCD declarations in the _master_ 
lib. Also it is ready to work in many other domains.


4) About computation of GCD.

Assume that we have chosen the above generic notion of GCD.

For ℕ, ℤ, Bin, ℤb there is known a simple method which is also 
reasonably efficient: an Euclidean algorithm.
This method is also applicable to all Euclidean domains.

For the multivariate polynomial domain R[x1..xn] for a GCD-domain R, the 
Euclidean method is not correct. But
* the above generic notion of GCD is valid there,
* CA libraries apply several more complex methods that have a reasonable 
efficiency.

Now, consider GCD computing only for ℕ, ℤ, Bin, and other Euclidean 
domains.
The standard library _needs_ to implement at least the first tree.

Integer.gcd  can be trivially reduced to computing Nat.GCD.gcd, probably 
this is done so in lib-1.1 (is it?).
Bin.GCD.gcd has to be implemented separately, because the conversion ℕ 
<--> Bin is expensive.

For ℕ and Bin, we need to define the notion(s) of DivMod (division with 
remainder), to implement the algorithm(s) of divMod, and implement GCD 
by applying divMod in recursion.

We need to choose between the two approaches.

4.1. Declare the DivMod notion separately for ℕ and Bin.
      Also the library needs DivMod for ℤ, independently on computing 
GCD.
So, we shall have the three declared notions of DivMod, the two 
algorithms for the divMod implementation, and the two implementations of 
GCD.

4.2. In the environment of CancellativeSemiring described above to 
introduce a general notion

   record EuclideanSemiring _ where
     C = Carrier
     field norm : Norm

     open Norm norm
     field
       divMod    : C → (y : C) → y ≉ 0# → C × C
       divModEq< : (a b : C) → (nzb : b ≉ 0#) →
                               let (q , r) = divMod a b nzb
                               in
                               a ≈ (q * b) + r  ×
                               ((nzr : r ≉ 0#) → ∣ r ,, nzr ∣ < ∣ b ,, 
nzb ∣)

A _norm_
          ∣_,,_∣ :  (x : C) → x ≉ 0# → ℕ
is a certain operation on nonzero elements (with a couple of certain 
declared laws). Examples:
For ℕ it can be set identic,
for ℤ it can be set absolute value,
for the univariate polynomial domain it can be set the _degree_.

This is a generic notion of DivMod. It covers the instances of ℕ, ℤ, 
Bin, ℤb, univariate polynomial domain over any field, Gaussian integers, 
and some others.
It also makes it easy to provide a common gcd  implementation for all 
these domains.

In textbooks, they define _Euclidean domain_ for this purpose.
But unfortunately this requires to inherit a Ring. And this does not 
cover the case of ℕ, because ℕ is not of Ring.

Therefore I suggest a slight generalization: the above 
EuclideanSemiring.

But there are needed the four additional axioms in EuclideanSemiring 
which are not in textbooks. They are specific to Agda: congruence of 
norm and of quotient, and irrelevance on the proof for x ≉ 0# for norm 
and quotient.


Summing up all the above, I suggest the following steps in developing 
the library.

1. To declare the generic divisibility notion _∣_ for Magma (this is 
simple).
    To implement the generic GCD notion for a CancellativeSemiring, as 
above.
    To remove the existing special definitions for divisibility and GCD.

2. To have the three special definitions Nat.DivMod.DivMod, 
Integer.DivMod.DivMod, Bin.DivMod.DivMod,
    and the two different implementations for the corresponding  divMod,
    the two different implementations for the corresponding  gcd.

3. After this is settled, to decide whether to change to the above 
EuclideanSemiring and thus to arrange a common
    implementation for GCD for ℕ, ℤ, Bin, ℤb, and other domains of  
EuclideanSemiring.

The first two points are verified, they are presented by a tested 
workable code, which is also simple.
The generic branch in the third point can be verified later, if we 
decide to try it (currently I doubt about keeping the code simple).

Please, consider the suggestion.

Regards,

------
Sergei




More information about the Agda mailing list