Euclid's Algorithm for the Greatest Common Divisor

We all know what the Greatest Common Divisor is: given two integers m and n, it is the largest integer g such that g*i = m and g*j = n, where i and j are also integers.

Here's an interesting fact about the GCD: if m >= n, then gcd(m, n) = gcd(m - n, n). We can show this by showing, first, that any common divisor of m and n is also a common divisor of m-n and n, and then that there is no larger common divisor of m-n and n than g (contradiction proof).

  1. given: g*i - g*j = g*(i - j), and i - j is still a positive integer. So g is a divisor of m-n and n.
  2. assume: the GCD of m-n and n were some integer g' > g. Then there would be integers i' and j' such that i' * g' = m-n and j' * g' = n. Since m = m-n + n, m = i'*g' + j'*g' = (i' + j') * g', so g' would be the GCD of m and n. Since this is a contradiction (g' would be = to g), there is no such g'.
A second fact we need to remember here is that gcd(m, 0) = m.

The last fact we will need is that gcd(m, n) == gcd(n, m).

Euclid found an algorithm that exploits these facts to calculate the GCD of two numbers M and N, as follows:


#define M 15
#define N 10

void euclid() {
    int gcd, tmp, m, n;

    m = M;
    n = N;

    // gcd(m, n) == gcd(M, N)
    while (n != 0) {
        // gcd(m, n) == gcd(M, N)
        if (m >= n)
            // gcd(m-n, n) == gcd(M, N)
            m = m - n;
        else {
            // (gcd(m, n) == gcd(M, N)) && (m > n)
            tmp = m;
            // gcd(tmp, n) == gcd(M, N)
            m = n;
            // gcd(tmp, m) == gcd(M, N)
            n = tmp;
            // gcd(n, m) == gcd(M, N)
            // gcd(m, n) == gcd(M, N)
        }
    }
    // (gcd(m, n) == gcd(M, N)) && (n == 0)
    // gcd(m, 0) == gcd(M, N)
    // n = gcd(M, N)
    gcd = m;
}

This algorithm has become one of the Grand Old Examples, because it's simple enough to use as an example, and at the same time complicated enough to require some thought. How can we convert this to assembly? First, let's convert to ``simple C.'' We'll do this pretty stupidly at first, then we'll optimize a little. We'll do the conversion by applying three rules:
  1. There can be no more than one arithmetic operation in a statement. If there are two, you have to break the statement up into subparts and use temporaries.
  2. If you are going to assign something to any variable other than a or b, you can only perform a simple assignment (e.g. res = a;) - you can't do any arithmetic and store the result there.
  3. At least one of the operands of any arithmetic operation must be a or b, and if it's not just a comparison, the result must go in a or b. What's more, if the operand is a then the result must go into a, and if the operand is b then the result must go into b.

#define M 15
#define N 10

void euclid() {
    int gcd, tmp, m, n, a, b;

    // m = M;
    a = M;
    m = a;

    // n = N;
    b = N;
    n = b;

    while (n != 0) {
        // if (m >= n) {
        a = m;
        if (a >= n) {

            // m = m - n
            a = m;
            a = a - n;
            m = a;

        } else {

            // tmp = m
            a = m;
            tmp = a;

            // m = n
            a = n;
            m = a;

            // n = tmp
            b = tmp;
            n = b;
        }
    }

    // gcd = m;
    a = m;
    gcd = a;
}

To optimize, we'll observe that every time we want to read m, a already has the value we were about to read in for it. Similarly, b already has the value for n every time we want to read that one. So, we can eliminate every place we read m and n, as follows:

#define M 15
#define N 10

void euclid() {
    int gcd, tmp, a, b;


    // m = M;
    a = M;
    // m = a;

    // n = N;
    b = N;
    // n = b;

    while (b != 0) {
        // if (m >= n) {
        if (a >= b) {

            // m = m - n
            a = a - b;

        } else {

            // tmp = m
            tmp = a;

            // m = n
            a = b;

            // n = tmp
            b = tmp;
        }
    }

    // gcd = m;
    gcd = a;
}