Skip to content

Commit

Permalink
Merge pull request #5350 from quickfur/issue7102a
Browse files Browse the repository at this point in the history
Fix issue 7102: std.numeric.gcd overload that works with non-builtin types
merged-on-behalf-of: Jack Stouffer <jack@jackstouffer.com>
  • Loading branch information
dlang-bot committed Apr 28, 2017
2 parents 540e143 + abf4e93 commit 35bbd36
Showing 1 changed file with 91 additions and 0 deletions.
91 changes: 91 additions & 0 deletions std/numeric.d
Expand Up @@ -2601,8 +2601,16 @@ GapWeightedSimilarityIncremental!(R, F) gapWeightedSimilarityIncremental(R, F)
Computes the greatest common divisor of $(D a) and $(D b) by using
an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm, Euclid's)
or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm.
Params:
T = Any numerical type that supports the modulo operator `%`. If
bit-shifting `<<` and `>>` are also supported, Stein's algorithm will
be used; otherwise, Euclid's algorithm is used as _a fallback.
Returns:
The greatest common divisor of the given arguments.
*/
T gcd(T)(T a, T b)
if (isIntegral!T)
{
static if (is(T == const) || is(T == immutable))
{
Expand Down Expand Up @@ -2655,6 +2663,89 @@ T gcd(T)(T a, T b)
assert(gcd(a, b) == 13);
}

// This overload is for non-builtin numerical types like BigInt or
// user-defined types.
/// ditto
T gcd(T)(T a, T b)
if (!isIntegral!T &&
is(typeof(T.init % T.init)) &&
is(typeof(T.init == 0 || T.init > 0)))
{
import std.algorithm.mutation : swap;

enum canUseBinaryGcd = is(typeof(() {
T t, u;
t <<= 1;
t >>= 1;
t -= u;
bool b = (t & 1) == 0;
swap(t, u);
}));

assert(a >= 0 && b >= 0);

static if (canUseBinaryGcd)
{
uint shift = 0;
while ((a & 1) == 0 && (b & 1) == 0)
{
a >>= 1;
b >>= 1;
shift++;
}

do
{
assert((a & 1) != 0);
while ((b & 1) == 0)
b >>= 1;
if (a > b)
swap(a, b);
b -= a;
} while (b);

return a << shift;
}
else
{
// The only thing we have is %; fallback to Euclidean algorithm.
while (b != 0)
{
auto t = b;
b = a % b;
a = t;
}
return a;
}
}

// Issue 7102
@system pure unittest
{
import std.bigint : BigInt;
assert(gcd(BigInt("71_000_000_000_000_000_000"),
BigInt("31_000_000_000_000_000_000")) ==
BigInt("1_000_000_000_000_000_000"));
}

@safe pure nothrow unittest
{
// A numerical type that only supports % and - (to force gcd implementation
// to use Euclidean algorithm).
struct CrippledInt
{
int impl;
CrippledInt opBinary(string op : "%")(CrippledInt i)
{
return CrippledInt(impl % i.impl);
}
int opEquals(CrippledInt i) { return impl == i.impl; }
int opEquals(int i) { return impl == i; }
int opCmp(int i) { return (impl < i) ? -1 : (impl > i) ? 1 : 0; }
}
assert(gcd(CrippledInt(2310), CrippledInt(1309)) == CrippledInt(77));
}

// This is to make tweaking the speed/size vs. accuracy tradeoff easy,
// though floats seem accurate enough for all practical purposes, since
// they pass the "approxEqual(inverseFft(fft(arr)), arr)" test even for
Expand Down

0 comments on commit 35bbd36

Please sign in to comment.