gcd.hpp

Back to Integers

pastel/sys/integer/

#ifndef PASTELSYS_GCD_HPP
#define PASTELSYS_GCD_HPP

#include "pastel/sys/integer/gcd.h"
#include "pastel/sys/math/number_tests.h"

namespace Pastel
{

    template <typename Integer>
    Integer extendedGcd(
        Integer a, Integer b,
        Integer& x, Integer& y)
    {
        x = 0;
        y = 1;
        Integer xLast = 1;
        Integer yLast = 0;

        while (b != 0)
        {
            Integer quotient = a / b;

            Integer temp = b;
            b = a % b;
            a = temp;

            temp = x;
            x = xLast - quotient * x;
            xLast = temp;

            temp = y;
            y = yLast - quotient * y;
            yLast = temp;
        }

        x = xLast;
        y = yLast;

        return a;
    }

    template <typename Integer>
    Integer gcd(const Integer& left, const Integer& right)
    {
        // This is the binary gcd algorithm.
        // For integers 'left' and 'right' this function
        // actually computes GCD(abs(left), abs(right)).
        // GCD(0, 0) is not defined and results in an error.

        // The binary gcd algorithm works by applying
        // a set of reducing identities until
        // we reach a base case, which is either GCD(0, x) or
        // GCD(x, 0).
        // For the following, keep in mind that GCD is symmetric:
        // GCD(u, v) = GCD(v, u)

        ENSURE(!(zero(left) && zero(right)));

        // For native Integer types.
        using std::abs;

        Integer u = abs(left);
        Integer v = abs(right);

        // GCD(0, x) = GCD(x, 0) = x

        if (zero(u))
        {
            return v;
        }

        if (zero(v))
        {
            return u;
        }

        // If u and v are both even, then
        // GCD(u / 2, v / 2) = 2 * GCD(u, v)

        // Remove the common powers of 2 out of u and v.
        // Store the 'shift' for later multiplication by '2^shift'.

        integer shift = 0;

        while (even(u) && even(v))
        {
            u >>= 1;
            v >>= 1;
            ++shift;
        }

        // Either u or v is (or both are) odd now.

        // If u is even and v is odd:
        // GCD(u / 2, v) = GCD(u, v)

        while (even(u))
        {
            u >>= 1;
        }

        // From here on, u is always odd.

        do
        {
            // If u is odd and v is even:
            // GCD(u, v / 2) = GCD(u, v)

            while (even(v))
            {
                v >>= 1;
            }

            // Now u and v are both odd, so 'u - v' is even.

            if (u < v)
            {
                // If u < v and u and v are both odd:
                // GCD(u, (v - u) / 2) = GCD(u, v)
                // The division by 2 is done later.

                v -= u;
            }
            else
            {
                // If u >= v and u and v are both odd:
                // GCD(v, (u - v) / 2) = GCD(u, v)
                // The division by 2 is done later.

                Integer difference(u - v);
                u = v;
                v = difference;
            }

            v >>= 1;
        }
        while (v != 0);

        // Multiply by the powers of 2 removed
        // in the beginning.

        return u << shift;
    }

}

#endif