From 6aea683103d9e528438edd7369006d054e9e7d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Tue, 26 Nov 2013 01:41:45 +0100 Subject: [PATCH 1/7] Fix 8167 (identity constructor) and 9061 (bitwise ops) --- std/bigint.d | 56 +++++++++++++++++++++++++++---- std/internal/math/biguintcore.d | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/std/bigint.d b/std/bigint.d index 6c4397f51..326abe1ea 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -111,6 +111,12 @@ public: data = data.init; // @@@: Workaround for compiler bug opAssign(x); } + + /// + this(T)(T x) pure if (is(Unqual!T == BigInt)) + { + opAssign(x); + } /// BigInt opAssign(T)(T x) pure if (isIntegral!T) @@ -131,7 +137,7 @@ public: // BigInt op= integer BigInt opOpAssign(string op, T)(T y) pure if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" - || op==">>" || op=="<<" || op=="^^") && isIntegral!T) + || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) { ulong u = absUnsign(y); @@ -196,13 +202,18 @@ public: sign = (y & 1) ? sign : false; data = BigUint.pow(data, u); } + else static if (op=="|" || op=="&" || op=="^") + { + BigInt b = y; + opOpAssign!op(b); + } else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported"); return this; } // BigInt op= BigInt BigInt opOpAssign(string op, T)(T y) pure - if ((op=="+" || op== "-" || op=="*" || op=="/" || op=="%") + if ((op=="+" || op== "-" || op=="*" || op=="/" || op=="%" || op=="|" || op=="&" || op=="^") && is (T: BigInt)) { static if (op == "+") @@ -238,13 +249,17 @@ public: sign = false; } } + else static if (op == "|" || op == "&" || op == "^") + { + data = BigUint.bitwiseOp!op(data, y.data, sign, y.sign, sign); + } else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported"); return this; } // BigInt op BigInt BigInt opBinary(string op, T)(T y) pure const - if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="%") + if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="%" || op=="|" || op=="&" || op=="^") && is (T: BigInt)) { BigInt r = this; @@ -253,7 +268,7 @@ public: // BigInt op integer BigInt opBinary(string op, T)(T y) pure const - if ((op=="+" || op == "*" || op=="-" || op=="/" + if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || op=="^" || op==">>" || op=="<<" || op=="^^") && isIntegral!T) { BigInt r = this; @@ -274,7 +289,7 @@ public: // Commutative operators BigInt opBinaryRight(string op, T)(T y) pure const - if ((op=="+" || op=="*") && isIntegral!T) + if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) { return opBinary!(op)(y); } @@ -318,7 +333,7 @@ public: } } // const unary operations - BigInt opUnary(string op)() pure const if (op=="+" || op=="-") + BigInt opUnary(string op)() pure const if (op=="+" || op=="-" || op=="~") { static if (op=="-") { @@ -326,6 +341,10 @@ public: r.negate(); return r; } + else static if (op=="~") + { + return -(this+1); + } else static if (op=="+") return this; } @@ -878,3 +897,28 @@ unittest // 11148 BigInt n = 2; n *= 2; } + +unittest // 8167 +{ + BigInt a = BigInt(3); + BigInt b = BigInt(a); +} + +unittest // 9061 +{ + long l1 = 0x12345678_90ABCDEF; + long l2 = 0xFEDCBA09_87654321; + long l3 = l1 | l2; + long l4 = l1 & l2; + long l5 = l1 ^ l2; + + BigInt b1 = l1; + BigInt b2 = l2; + BigInt b3 = b1 | b2; + BigInt b4 = b1 & b2; + BigInt b5 = b1 ^ b2; + + assert(l3 == b3); + assert(l4 == b4); + assert(l5 == b5); +} \ No newline at end of file diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index 74f8f689e..127cb403e 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -635,6 +635,27 @@ public: divModInternal(result, rem, x.data, y.data); return BigUint(removeLeadingZeros(assumeUnique(rem))); } + + // return x op y + static BigUint bitwiseOp(string op)(BigUint x, BigUint y, bool xSign, bool ySign, ref bool resultSign) pure if (op == "|" || op == "^" || op == "&") + { + // Add one to length in case the 'virtual sign bit' is used. Strictly speaking not always necessary. + auto d1 = includeSign(x.data, y.uintLength+1, xSign); + auto d2 = includeSign(y.data, x.uintLength+1, ySign); + + foreach (i; 0..d1.length) + { + mixin("d1[i] " ~ op ~ "= d2[i];"); + } + + mixin("resultSign = xSign " ~ op ~ " ySign;"); + + if (resultSign) { + d1 = twosComplement(d1, d1.length); + } + + return BigUint(removeLeadingZeros(assumeUnique(d1))); + } /** * Return a BigUint which is x raised to the power of y. @@ -943,6 +964,44 @@ unittest private: +BigDigit[] twosComplement(const(BigDigit) [] x, int length) pure { + BigDigit [] result = new BigDigit[length]; + + foreach (i; 0..x.length) + { + result[i] = ~x[i]; + } + result[x.length..$] = BigDigit.max; + + bool sgn = false; + BigUint tmp = BigUint.addOrSubInt(BigUint(assumeUnique(result)), 1UL, false, sgn); + return cast(BigDigit[])tmp.data; +} + +// Encode BigInt as BigDigit array (sign and 2's complement) +BigDigit[] includeSign(const(BigDigit) [] x, int minSize, bool sign) pure +{ + int length = -1; + if (x.length > minSize) { + length = x.length; + + // Virtual sign bit. If this is in use, we need to use one more BigDigit. + if (x[$-1] > (BigDigit.max >> 1)) { + length++; + } + } else { + length = minSize; + } + if (sign) { + return twosComplement(x, length); + } + else + { + BigDigit [] result = new BigDigit[length]; + result[0..x.length] = x; + return result; + } +} // works for any type T intpow(T)(T x, ulong n) pure From ed5a2868269dc801c767b64d27c632e352d1ff0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Tue, 26 Nov 2013 17:50:44 +0100 Subject: [PATCH 2/7] Removed unnecessary allocation and computation --- std/internal/math/biguintcore.d | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index 127cb403e..8aa20f94e 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -639,9 +639,8 @@ public: // return x op y static BigUint bitwiseOp(string op)(BigUint x, BigUint y, bool xSign, bool ySign, ref bool resultSign) pure if (op == "|" || op == "^" || op == "&") { - // Add one to length in case the 'virtual sign bit' is used. Strictly speaking not always necessary. - auto d1 = includeSign(x.data, y.uintLength+1, xSign); - auto d2 = includeSign(y.data, x.uintLength+1, ySign); + auto d1 = includeSign(x.data, y.uintLength, xSign); + auto d2 = includeSign(y.data, x.uintLength, ySign); foreach (i; 0..d1.length) { @@ -981,17 +980,7 @@ BigDigit[] twosComplement(const(BigDigit) [] x, int length) pure { // Encode BigInt as BigDigit array (sign and 2's complement) BigDigit[] includeSign(const(BigDigit) [] x, int minSize, bool sign) pure { - int length = -1; - if (x.length > minSize) { - length = x.length; - - // Virtual sign bit. If this is in use, we need to use one more BigDigit. - if (x[$-1] > (BigDigit.max >> 1)) { - length++; - } - } else { - length = minSize; - } + int length = (x.length > minSize) ? x.length : minSize; if (sign) { return twosComplement(x, length); } From 52fc3fb3e6fb31e6f133118b8d2f78664f848d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Wed, 27 Nov 2013 09:21:01 +0100 Subject: [PATCH 3/7] Fixed compilation errors on 64-bit platforms (hopefully) --- std/internal/math/biguintcore.d | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index 8aa20f94e..1e49e2572 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -963,7 +963,9 @@ unittest private: -BigDigit[] twosComplement(const(BigDigit) [] x, int length) pure { +BigDigit[] twosComplement(const(BigDigit) [] x, size_t length) pure +{ + assert(length != 0); BigDigit [] result = new BigDigit[length]; foreach (i; 0..x.length) @@ -978,10 +980,11 @@ BigDigit[] twosComplement(const(BigDigit) [] x, int length) pure { } // Encode BigInt as BigDigit array (sign and 2's complement) -BigDigit[] includeSign(const(BigDigit) [] x, int minSize, bool sign) pure +BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign) pure { - int length = (x.length > minSize) ? x.length : minSize; - if (sign) { + size_t length = (x.length > minSize) ? x.length : minSize; + if (sign) + { return twosComplement(x, length); } else @@ -2284,7 +2287,6 @@ version(unittest) unittest { - void printBiguint(const uint [] data) { char [] buff = biguintToHex(new char[data.length*9], data, '_'); @@ -2315,4 +2317,4 @@ unittest r = b[0..a.length]; assert(r[] == r1[]); assert(q[] == q1[]); -} +} \ No newline at end of file From 92560323dd3d36dc1b2256e91dfd80b209e4ddad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Sat, 14 Dec 2013 19:54:18 +0100 Subject: [PATCH 4/7] Got rid of expensive AddOrSubInt call (and related allocation). --- std/internal/math/biguintcore.d | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index 7c82feec8..2a019729c 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -979,8 +979,16 @@ BigDigit[] twosComplement(const(BigDigit) [] x, size_t length) pure result[x.length..$] = BigDigit.max; bool sgn = false; - BigUint tmp = BigUint.addOrSubInt(BigUint(assumeUnique(result)), 1UL, false, sgn); - return cast(BigDigit[])tmp.data; + + foreach (i; 0..length) { + if (result[i] == BigDigit.max) { + result[i] = 0; + } else { + result[i] += 1; + break; + } + } + return result; } // Encode BigInt as BigDigit array (sign and 2's complement) From c67cd90894f03d2f7c36ae6e7e6d935dfabec9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Sat, 14 Dec 2013 19:59:25 +0100 Subject: [PATCH 5/7] Added description of logical (bitwise) operations. --- std/bigint.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/bigint.d b/std/bigint.d index aff40dada..54bbcf5d3 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -32,7 +32,7 @@ private import std.traits; /** A struct representing an arbitrary precision integer * * All arithmetic operations are supported, except - * unsigned shift right (>>>). Logical operations are not currently supported. + * unsigned shift right (>>>). Logical operations (|, &, ^, ~) are supported, and behave as if BigInt was an infinite length 2's complement number. * * BigInt implements value semantics using copy-on-write. This means that * assignment is cheap, but operations such as x++ will cause heap From 8412f4d37a78addb226d971619d0842110ae132b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Sat, 14 Dec 2013 20:08:43 +0100 Subject: [PATCH 6/7] Removed unnecessary allocations in twosComplement call. --- std/internal/math/biguintcore.d | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index 2a019729c..8fc4dee07 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -654,7 +654,7 @@ public: mixin("resultSign = xSign " ~ op ~ " ySign;"); if (resultSign) { - d1 = twosComplement(d1, d1.length); + twosComplement(d1, d1); } return BigUint(removeLeadingZeros(assumeUnique(d1))); @@ -967,11 +967,8 @@ unittest private: -BigDigit[] twosComplement(const(BigDigit) [] x, size_t length) pure +void twosComplement(const(BigDigit) [] x, BigDigit[] result) pure { - assert(length != 0); - BigDigit [] result = new BigDigit[length]; - foreach (i; 0..x.length) { result[i] = ~x[i]; @@ -980,7 +977,7 @@ BigDigit[] twosComplement(const(BigDigit) [] x, size_t length) pure bool sgn = false; - foreach (i; 0..length) { + foreach (i; 0..result.length) { if (result[i] == BigDigit.max) { result[i] = 0; } else { @@ -988,23 +985,22 @@ BigDigit[] twosComplement(const(BigDigit) [] x, size_t length) pure break; } } - return result; } // Encode BigInt as BigDigit array (sign and 2's complement) BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign) pure { size_t length = (x.length > minSize) ? x.length : minSize; + BigDigit [] result = new BigDigit[length]; if (sign) { - return twosComplement(x, length); + twosComplement(x, result); } else { - BigDigit [] result = new BigDigit[length]; result[0..x.length] = x; - return result; } + return result; } // works for any type From 94489cb09cfc3d860ad64a933fce753fc85e8687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Kj=C3=A6r=C3=A5s?= Date: Tue, 17 Dec 2013 12:54:35 +0100 Subject: [PATCH 7/7] Fix name of bitwise operations --- std/bigint.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/bigint.d b/std/bigint.d index 54bbcf5d3..a9fd0d421 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -32,7 +32,7 @@ private import std.traits; /** A struct representing an arbitrary precision integer * * All arithmetic operations are supported, except - * unsigned shift right (>>>). Logical operations (|, &, ^, ~) are supported, and behave as if BigInt was an infinite length 2's complement number. + * unsigned shift right (>>>). Bitwise operations (|, &, ^, ~) are supported, and behave as if BigInt was an infinite length 2's complement number. * * BigInt implements value semantics using copy-on-write. This means that * assignment is cheap, but operations such as x++ will cause heap