From 339fe619e7c6c107cdc4ec76dbba0f49b7c9f892 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Tue, 25 Apr 2017 15:00:07 -0700 Subject: [PATCH 1/4] Add missing sig constraints to std.numeric.gcd. First of all, it makes no sense to take arbitrary types (e.g., gcd("a","abc") should not even match the template). Second of all, the current implementation only works with built-in types, so the function should restrict itself to only accepting built-in types so that it is overloadable for other types (e.g., BigInt). --- std/numeric.d | 1 + 1 file changed, 1 insertion(+) diff --git a/std/numeric.d b/std/numeric.d index 1274f74a848..151e2b503c6 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2603,6 +2603,7 @@ an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm. */ T gcd(T)(T a, T b) + if (isIntegral!T) { static if (is(T == const) || is(T == immutable)) { From 9fe2ede16db44a8ac544aaca1910174b6116ba7c Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Tue, 25 Apr 2017 16:58:33 -0700 Subject: [PATCH 2/4] Implement generic gcd for non-builtin types. --- std/numeric.d | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/std/numeric.d b/std/numeric.d index 151e2b503c6..4a99b29655a 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2656,6 +2656,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 +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")); +} + +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 From e0f94770479e81d9fed0f84cd74897f7f7655ac3 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Tue, 25 Apr 2017 17:06:57 -0700 Subject: [PATCH 3/4] Documentation. --- std/numeric.d | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/std/numeric.d b/std/numeric.d index 4a99b29655a..b24fa280604 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2601,6 +2601,13 @@ 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) From abf4e93fa0de5c7d001e39dc04d5fdbe07d22135 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Wed, 26 Apr 2017 09:38:57 -0700 Subject: [PATCH 4/4] Annotate unittests per Phobos coding style. --- std/numeric.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/numeric.d b/std/numeric.d index b24fa280604..c6cea6fc608 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2720,7 +2720,7 @@ T gcd(T)(T a, T b) } // Issue 7102 -unittest +@system pure unittest { import std.bigint : BigInt; assert(gcd(BigInt("71_000_000_000_000_000_000"), @@ -2728,7 +2728,7 @@ unittest BigInt("1_000_000_000_000_000_000")); } -unittest +@safe pure nothrow unittest { // A numerical type that only supports % and - (to force gcd implementation // to use Euclidean algorithm).