#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