aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/bigint.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/bigint.d')
-rw-r--r--libphobos/src/std/bigint.d1705
1 files changed, 1705 insertions, 0 deletions
diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d
new file mode 100644
index 0000000..4c518f2
--- /dev/null
+++ b/libphobos/src/std/bigint.d
@@ -0,0 +1,1705 @@
+/** Arbitrary-precision ('bignum') arithmetic.
+ *
+ * Performance is optimized for numbers below ~1000 decimal digits.
+ * For X86 machines, highly optimised assembly routines are used.
+ *
+ * The following algorithms are currently implemented:
+ * $(UL
+ * $(LI Karatsuba multiplication)
+ * $(LI Squaring is optimized independently of multiplication)
+ * $(LI Divide-and-conquer division)
+ * $(LI Binary exponentiation)
+ * )
+ *
+ * For very large numbers, consider using the $(HTTP gmplib.org, GMP library) instead.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Don Clugston
+ * Source: $(PHOBOSSRC std/_bigint.d)
+ */
+/* Copyright Don Clugston 2008 - 2010.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+module std.bigint;
+
+import std.conv : ConvException;
+
+import std.format : FormatSpec, FormatException;
+import std.internal.math.biguintcore;
+import std.range.primitives;
+import std.traits;
+
+/** A struct representing an arbitrary precision integer.
+ *
+ * All arithmetic operations are supported, except 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
+ * allocation. (But note that for most bigint operations, heap allocation is
+ * inevitable anyway.)
+ */
+struct BigInt
+{
+private:
+ BigUint data; // BigInt adds signed arithmetic to BigUint.
+ bool sign = false;
+public:
+ /**
+ * Construct a BigInt from a decimal or hexadecimal string. The number must
+ * be in the form of a decimal or hex literal. It may have a leading `+`
+ * or `-` sign, followed by `0x` or `0X` if hexadecimal. Underscores are
+ * permitted in any location after the `0x` and/or the sign of the number.
+ *
+ * Params:
+ * s = a finite bidirectional range of any character type
+ *
+ * Throws:
+ * $(REF ConvException, std,conv) if the string doesn't represent a valid number
+ */
+ this(Range)(Range s) if (
+ isBidirectionalRange!Range &&
+ isSomeChar!(ElementType!Range) &&
+ !isInfinite!Range &&
+ !isSomeString!Range)
+ {
+ import std.algorithm.iteration : filterBidirectional;
+ import std.algorithm.searching : startsWith;
+ import std.conv : ConvException;
+ import std.exception : enforce;
+ import std.utf : byChar;
+
+ enforce!ConvException(!s.empty, "Can't initialize BigInt with an empty range");
+
+ bool neg = false;
+ bool ok;
+
+ data = 0UL;
+
+ // check for signs and if the string is a hex value
+ if (s.front == '+')
+ {
+ s.popFront(); // skip '+'
+ }
+ else if (s.front == '-')
+ {
+ neg = true;
+ s.popFront();
+ }
+
+ if (s.save.startsWith("0x".byChar) ||
+ s.save.startsWith("0X".byChar))
+ {
+ s.popFront;
+ s.popFront;
+
+ if (!s.empty)
+ ok = data.fromHexString(s.filterBidirectional!(a => a != '_'));
+ else
+ ok = false;
+ }
+ else
+ {
+ ok = data.fromDecimalString(s.filterBidirectional!(a => a != '_'));
+ }
+
+ enforce!ConvException(ok, "Not a valid numerical string");
+
+ if (isZero())
+ neg = false;
+
+ sign = neg;
+ }
+
+ /// ditto
+ this(Range)(Range s) pure if (isSomeString!Range)
+ {
+ import std.utf : byCodeUnit;
+ this(s.byCodeUnit);
+ }
+
+ @system unittest
+ {
+ // system because of the dummy ranges eventually call std.array!string
+ import std.exception : assertThrown;
+ import std.internal.test.dummyrange;
+
+ auto r1 = new ReferenceBidirectionalRange!dchar("101");
+ auto big1 = BigInt(r1);
+ assert(big1 == BigInt(101));
+
+ auto r2 = new ReferenceBidirectionalRange!dchar("1_000");
+ auto big2 = BigInt(r2);
+ assert(big2 == BigInt(1000));
+
+ auto r3 = new ReferenceBidirectionalRange!dchar("0x0");
+ auto big3 = BigInt(r3);
+ assert(big3 == BigInt(0));
+
+ auto r4 = new ReferenceBidirectionalRange!dchar("0x");
+ assertThrown!ConvException(BigInt(r4));
+ }
+
+ /// Construct a BigInt from a built-in integral type.
+ this(T)(T x) pure nothrow if (isIntegral!T)
+ {
+ data = data.init; // @@@: Workaround for compiler bug
+ opAssign(x);
+ }
+
+ ///
+ @system unittest
+ {
+ // @system due to failure in FreeBSD32
+ ulong data = 1_000_000_000_000;
+ auto bigData = BigInt(data);
+ assert(data == BigInt("1_000_000_000_000"));
+ }
+
+ /// Construct a BigInt from another BigInt.
+ this(T)(T x) pure nothrow if (is(Unqual!T == BigInt))
+ {
+ opAssign(x);
+ }
+
+ ///
+ @system unittest
+ {
+ const(BigInt) b1 = BigInt("1_234_567_890");
+ BigInt b2 = BigInt(b1);
+ assert(b2 == BigInt("1_234_567_890"));
+ }
+
+ /// Assignment from built-in integer types.
+ BigInt opAssign(T)(T x) pure nothrow if (isIntegral!T)
+ {
+ data = cast(ulong) absUnsign(x);
+ sign = (x < 0);
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ auto b = BigInt("123");
+ b = 456;
+ assert(b == BigInt("456"));
+ }
+
+ /// Assignment from another BigInt.
+ BigInt opAssign(T:BigInt)(T x) pure @nogc
+ {
+ data = x.data;
+ sign = x.sign;
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ auto b1 = BigInt("123");
+ auto b2 = BigInt("456");
+ b2 = b1;
+ assert(b2 == BigInt("123"));
+ }
+
+ /**
+ * Implements assignment operators from built-in integers of the form
+ * $(D BigInt op= integer).
+ */
+ BigInt opOpAssign(string op, T)(T y) pure nothrow
+ if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
+ || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T)
+ {
+ ulong u = absUnsign(y);
+
+ static if (op=="+")
+ {
+ data = BigUint.addOrSubInt(data, u, sign != (y<0), sign);
+ }
+ else static if (op=="-")
+ {
+ data = BigUint.addOrSubInt(data, u, sign == (y<0), sign);
+ }
+ else static if (op=="*")
+ {
+ if (y == 0)
+ {
+ sign = false;
+ data = 0UL;
+ }
+ else
+ {
+ sign = ( sign != (y<0) );
+ data = BigUint.mulInt(data, u);
+ }
+ }
+ else static if (op=="/")
+ {
+ assert(y != 0, "Division by zero");
+ static if (T.sizeof <= uint.sizeof)
+ {
+ data = BigUint.divInt(data, cast(uint) u);
+ }
+ else
+ {
+ data = BigUint.divInt(data, u);
+ }
+ sign = data.isZero() ? false : sign ^ (y < 0);
+ }
+ else static if (op=="%")
+ {
+ assert(y != 0, "Division by zero");
+ static if (is(immutable(T) == immutable(long)) || is( immutable(T) == immutable(ulong) ))
+ {
+ this %= BigInt(y);
+ }
+ else
+ {
+ data = cast(ulong) BigUint.modInt(data, cast(uint) u);
+ if (data.isZero())
+ sign = false;
+ }
+ // x%y always has the same sign as x.
+ // This is not the same as mathematical mod.
+ }
+ else static if (op==">>" || op=="<<")
+ {
+ // Do a left shift if y>0 and <<, or
+ // if y<0 and >>; else do a right shift.
+ if (y == 0)
+ return this;
+ else if ((y > 0) == (op=="<<"))
+ {
+ // Sign never changes during left shift
+ data = data.opShl(u);
+ } else
+ {
+ data = data.opShr(u);
+ if (data.isZero())
+ sign = false;
+ }
+ }
+ else static if (op=="^^")
+ {
+ 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;
+ }
+
+ ///
+ @system unittest
+ {
+ //@system because opOpAssign is @system
+ auto b = BigInt("1_000_000_000");
+
+ b += 12345;
+ assert(b == BigInt("1_000_012_345"));
+
+ b /= 5;
+ assert(b == BigInt("200_002_469"));
+ }
+
+ /**
+ * Implements assignment operators of the form $(D BigInt op= BigInt).
+ */
+ BigInt opOpAssign(string op, T)(T y) pure nothrow
+ if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%")
+ && is (T: BigInt))
+ {
+ static if (op == "+")
+ {
+ data = BigUint.addOrSub(data, y.data, sign != y.sign, &sign);
+ }
+ else static if (op == "-")
+ {
+ data = BigUint.addOrSub(data, y.data, sign == y.sign, &sign);
+ }
+ else static if (op == "*")
+ {
+ data = BigUint.mul(data, y.data);
+ sign = isZero() ? false : sign ^ y.sign;
+ }
+ else static if (op == "/")
+ {
+ y.checkDivByZero();
+ if (!isZero())
+ {
+ data = BigUint.div(data, y.data);
+ sign = isZero() ? false : sign ^ y.sign;
+ }
+ }
+ else static if (op == "%")
+ {
+ y.checkDivByZero();
+ if (!isZero())
+ {
+ data = BigUint.mod(data, y.data);
+ // x%y always has the same sign as x.
+ if (isZero())
+ 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;
+ }
+
+ ///
+ @system unittest
+ {
+ // @system because opOpAssign is @system
+ auto x = BigInt("123");
+ auto y = BigInt("321");
+ x += y;
+ assert(x == BigInt("444"));
+ }
+
+ /**
+ * Implements binary operators between BigInts.
+ */
+ BigInt opBinary(string op, T)(T y) pure nothrow const
+ if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" ||
+ op=="/" || op=="%")
+ && is (T: BigInt))
+ {
+ BigInt r = this;
+ return r.opOpAssign!(op)(y);
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("123");
+ auto y = BigInt("456");
+ BigInt z = x * y;
+ assert(z == BigInt("56088"));
+ }
+
+ /**
+ * Implements binary operators between BigInt's and built-in integers.
+ */
+ BigInt opBinary(string op, T)(T y) pure nothrow const
+ if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" ||
+ op=="^"|| op==">>" || op=="<<" || op=="^^")
+ && isIntegral!T)
+ {
+ BigInt r = this;
+ return r.opOpAssign!(op)(y);
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("123");
+ x *= 300;
+ assert(x == BigInt("36900"));
+ }
+
+ /**
+ Implements a narrowing remainder operation with built-in integer types.
+
+ This binary operator returns a narrower, built-in integer type
+ where applicable, according to the following table.
+
+ $(TABLE ,
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `long`) $(TD $(RARR)) $(TD `long`))
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `ulong`) $(TD $(RARR)) $(TD `BigInt`))
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD other type) $(TD $(RARR)) $(TD `int`))
+ )
+ */
+ auto opBinary(string op, T)(T y) pure nothrow const
+ if (op == "%" && isIntegral!T)
+ {
+ assert(y != 0);
+
+ // BigInt % long => long
+ // BigInt % ulong => BigInt
+ // BigInt % other_type => int
+ static if (is(Unqual!T == long) || is(Unqual!T == ulong))
+ {
+ auto r = this % BigInt(y);
+
+ static if (is(Unqual!T == long))
+ {
+ return r.toLong();
+ }
+ else
+ {
+ // return as-is to avoid overflow
+ return r;
+ }
+ }
+ else
+ {
+ immutable uint u = absUnsign(y);
+ int rem = BigUint.modInt(data, u);
+ // x%y always has the same sign as x.
+ // This is not the same as mathematical mod.
+ return sign ? -rem : rem;
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("1_000_000_500");
+ long l = 1_000_000L;
+ ulong ul = 2_000_000UL;
+ int i = 500_000;
+ short s = 30_000;
+
+ assert(is(typeof(x % l) == long) && x % l == 500L);
+ assert(is(typeof(x % ul) == BigInt) && x % ul == BigInt(500));
+ assert(is(typeof(x % i) == int) && x % i == 500);
+ assert(is(typeof(x % s) == int) && x % s == 10500);
+ }
+
+ /**
+ Implements operators with built-in integers on the left-hand side and
+ BigInt on the right-hand side.
+ */
+ BigInt opBinaryRight(string op, T)(T y) pure nothrow const
+ if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T)
+ {
+ return opBinary!(op)(y);
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("100");
+ BigInt y = 123 + x;
+ assert(y == BigInt("223"));
+
+ BigInt z = 123 - x;
+ assert(z == BigInt("23"));
+
+ // Dividing a built-in integer type by BigInt always results in
+ // something that fits in a built-in type, so the built-in type is
+ // returned, not BigInt.
+ assert(is(typeof(1000 / x) == int));
+ assert(1000 / x == 10);
+ }
+
+ // BigInt = integer op BigInt
+ /// ditto
+ BigInt opBinaryRight(string op, T)(T y) pure nothrow const
+ if (op == "-" && isIntegral!T)
+ {
+ ulong u = absUnsign(y);
+ BigInt r;
+ static if (op == "-")
+ {
+ r.sign = sign;
+ r.data = BigUint.addOrSubInt(data, u, sign == (y<0), r.sign);
+ r.negate();
+ }
+ return r;
+ }
+
+ // integer = integer op BigInt
+ /// ditto
+ T opBinaryRight(string op, T)(T x) pure nothrow const
+ if ((op=="%" || op=="/") && isIntegral!T)
+ {
+ checkDivByZero();
+
+ static if (op == "%")
+ {
+ // x%y always has the same sign as x.
+ if (data.ulongLength > 1)
+ return x;
+ immutable u = absUnsign(x);
+ immutable rem = u % data.peekUlong(0);
+ // x%y always has the same sign as x.
+ return cast(T)((x<0) ? -rem : rem);
+ }
+ else static if (op == "/")
+ {
+ if (data.ulongLength > 1)
+ return 0;
+ return cast(T)(x / data.peekUlong(0));
+ }
+ }
+
+ // const unary operations
+ /**
+ Implements BigInt unary operators.
+ */
+ BigInt opUnary(string op)() pure nothrow const if (op=="+" || op=="-" || op=="~")
+ {
+ static if (op=="-")
+ {
+ BigInt r = this;
+ r.negate();
+ return r;
+ }
+ else static if (op=="~")
+ {
+ return -(this+1);
+ }
+ else static if (op=="+")
+ return this;
+ }
+
+ // non-const unary operations
+ /// ditto
+ BigInt opUnary(string op)() pure nothrow if (op=="++" || op=="--")
+ {
+ static if (op=="++")
+ {
+ data = BigUint.addOrSubInt(data, 1UL, sign, sign);
+ return this;
+ }
+ else static if (op=="--")
+ {
+ data = BigUint.addOrSubInt(data, 1UL, !sign, sign);
+ return this;
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("1234");
+ assert(-x == BigInt("-1234"));
+
+ ++x;
+ assert(x == BigInt("1235"));
+ }
+
+ /**
+ Implements BigInt equality test with other BigInt's and built-in
+ integer types.
+ */
+ bool opEquals()(auto ref const BigInt y) const pure @nogc
+ {
+ return sign == y.sign && y.data == data;
+ }
+
+ /// ditto
+ bool opEquals(T)(T y) const pure nothrow @nogc if (isIntegral!T)
+ {
+ if (sign != (y<0))
+ return 0;
+ return data.opEquals(cast(ulong) absUnsign(y));
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("12345");
+ auto y = BigInt("12340");
+ int z = 12345;
+ int w = 54321;
+
+ assert(x == x);
+ assert(x != y);
+ assert(x == y + 5);
+ assert(x == z);
+ assert(x != w);
+ }
+
+ /**
+ Implements casting to bool.
+ */
+ T opCast(T:bool)() pure nothrow @nogc const
+ {
+ return !isZero();
+ }
+
+ ///
+ @system unittest
+ {
+ // Non-zero values are regarded as true
+ auto x = BigInt("1");
+ auto y = BigInt("10");
+ assert(x);
+ assert(y);
+
+ // Zero value is regarded as false
+ auto z = BigInt("0");
+ assert(!z);
+ }
+
+ /**
+ Implements casting to integer types.
+
+ Throws: $(REF ConvOverflowException, std,conv) if the number exceeds
+ the target type's range.
+ */
+ T opCast(T:ulong)() /*pure*/ const
+ {
+ if (isUnsigned!T && sign)
+ { /* throw */ }
+ else
+ if (data.ulongLength == 1)
+ {
+ ulong l = data.peekUlong(0);
+ if (isUnsigned!T || !sign)
+ {
+ if (l <= T.max)
+ return cast(T) l;
+ }
+ else
+ {
+ if (l <= ulong(T.max)+1)
+ return cast(T)-long(l); // -long.min == long.min
+ }
+ }
+
+ import std.conv : ConvOverflowException;
+ import std.string : format;
+ throw new ConvOverflowException(
+ "BigInt(%d) cannot be represented as a %s"
+ .format(this, T.stringof));
+ }
+
+ ///
+ @system unittest
+ {
+ import std.conv : to, ConvOverflowException;
+ import std.exception : assertThrown;
+
+ assert(BigInt("0").to!int == 0);
+
+ assert(BigInt("0").to!ubyte == 0);
+ assert(BigInt("255").to!ubyte == 255);
+ assertThrown!ConvOverflowException(BigInt("256").to!ubyte);
+ assertThrown!ConvOverflowException(BigInt("-1").to!ubyte);
+ }
+
+ @system unittest
+ {
+ import std.conv : to, ConvOverflowException;
+ import std.exception : assertThrown;
+
+ assert(BigInt("-1").to!byte == -1);
+ assert(BigInt("-128").to!byte == -128);
+ assert(BigInt("127").to!byte == 127);
+ assertThrown!ConvOverflowException(BigInt("-129").to!byte);
+ assertThrown!ConvOverflowException(BigInt("128").to!byte);
+
+ assert(BigInt("0").to!uint == 0);
+ assert(BigInt("4294967295").to!uint == uint.max);
+ assertThrown!ConvOverflowException(BigInt("4294967296").to!uint);
+ assertThrown!ConvOverflowException(BigInt("-1").to!uint);
+
+ assert(BigInt("-1").to!int == -1);
+ assert(BigInt("-2147483648").to!int == int.min);
+ assert(BigInt("2147483647").to!int == int.max);
+ assertThrown!ConvOverflowException(BigInt("-2147483649").to!int);
+ assertThrown!ConvOverflowException(BigInt("2147483648").to!int);
+
+ assert(BigInt("0").to!ulong == 0);
+ assert(BigInt("18446744073709551615").to!ulong == ulong.max);
+ assertThrown!ConvOverflowException(BigInt("18446744073709551616").to!ulong);
+ assertThrown!ConvOverflowException(BigInt("-1").to!ulong);
+
+ assert(BigInt("-1").to!long == -1);
+ assert(BigInt("-9223372036854775808").to!long == long.min);
+ assert(BigInt("9223372036854775807").to!long == long.max);
+ assertThrown!ConvOverflowException(BigInt("-9223372036854775809").to!long);
+ assertThrown!ConvOverflowException(BigInt("9223372036854775808").to!long);
+ }
+
+ /**
+ Implements casting to/from qualified BigInt's.
+
+ Warning: Casting to/from $(D const) or $(D immutable) may break type
+ system guarantees. Use with care.
+ */
+ T opCast(T)() pure nothrow @nogc const
+ if (is(Unqual!T == BigInt))
+ {
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ const(BigInt) x = BigInt("123");
+ BigInt y = cast() x; // cast away const
+ assert(y == x);
+ }
+
+ // Hack to make BigInt's typeinfo.compare work properly.
+ // Note that this must appear before the other opCmp overloads, otherwise
+ // DMD won't find it.
+ /**
+ Implements 3-way comparisons of BigInt with BigInt or BigInt with
+ built-in integers.
+ */
+ int opCmp(ref const BigInt y) pure nothrow @nogc const
+ {
+ // Simply redirect to the "real" opCmp implementation.
+ return this.opCmp!BigInt(y);
+ }
+
+ /// ditto
+ int opCmp(T)(T y) pure nothrow @nogc const if (isIntegral!T)
+ {
+ if (sign != (y<0) )
+ return sign ? -1 : 1;
+ int cmp = data.opCmp(cast(ulong) absUnsign(y));
+ return sign? -cmp: cmp;
+ }
+ /// ditto
+ int opCmp(T:BigInt)(const T y) pure nothrow @nogc const
+ {
+ if (sign != y.sign)
+ return sign ? -1 : 1;
+ immutable cmp = data.opCmp(y.data);
+ return sign? -cmp: cmp;
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("100");
+ auto y = BigInt("10");
+ int z = 50;
+ const int w = 200;
+
+ assert(y < x);
+ assert(x > z);
+ assert(z > y);
+ assert(x < w);
+ }
+
+ /**
+ Returns: The value of this BigInt as a long, or +/- long.max if outside
+ the representable range.
+ */
+ long toLong() @safe pure nothrow const @nogc
+ {
+ return (sign ? -1 : 1) *
+ (data.ulongLength == 1 && (data.peekUlong(0) <= sign+cast(ulong)(long.max)) // 1+long.max = |long.min|
+ ? cast(long)(data.peekUlong(0))
+ : long.max);
+ }
+
+ ///
+ @system unittest
+ {
+ auto b = BigInt("12345");
+ long l = b.toLong();
+ assert(l == 12345);
+ }
+
+ /**
+ Returns: The value of this BigInt as an int, or +/- int.max if outside
+ the representable range.
+ */
+ int toInt() @safe pure nothrow @nogc const
+ {
+ return (sign ? -1 : 1) *
+ (data.uintLength == 1 && (data.peekUint(0) <= sign+cast(uint)(int.max)) // 1+int.max = |int.min|
+ ? cast(int)(data.peekUint(0))
+ : int.max);
+ }
+
+ ///
+ @system unittest
+ {
+ auto big = BigInt("5_000_000");
+ auto i = big.toInt();
+ assert(i == 5_000_000);
+
+ // Numbers that are too big to fit into an int will be clamped to int.max.
+ auto tooBig = BigInt("5_000_000_000");
+ i = tooBig.toInt();
+ assert(i == int.max);
+ }
+
+ /// Number of significant uints which are used in storing this number.
+ /// The absolute value of this BigInt is always &lt; 2$(SUPERSCRIPT 32*uintLength)
+ @property size_t uintLength() @safe pure nothrow @nogc const
+ {
+ return data.uintLength;
+ }
+
+ /// Number of significant ulongs which are used in storing this number.
+ /// The absolute value of this BigInt is always &lt; 2$(SUPERSCRIPT 64*ulongLength)
+ @property size_t ulongLength() @safe pure nothrow @nogc const
+ {
+ return data.ulongLength;
+ }
+
+ /** Convert the BigInt to string, passing it to the given sink.
+ *
+ * Params:
+ * sink = A delegate for accepting possibly piecewise segments of the
+ * formatted string.
+ * formatString = A format string specifying the output format.
+ *
+ * $(TABLE Available output formats:,
+ * $(TR $(TD "d") $(TD Decimal))
+ * $(TR $(TD "o") $(TD Octal))
+ * $(TR $(TD "x") $(TD Hexadecimal, lower case))
+ * $(TR $(TD "X") $(TD Hexadecimal, upper case))
+ * $(TR $(TD "s") $(TD Default formatting (same as "d") ))
+ * $(TR $(TD null) $(TD Default formatting (same as "d") ))
+ * )
+ */
+ void toString(scope void delegate(const (char)[]) sink, string formatString) const
+ {
+ auto f = FormatSpec!char(formatString);
+ f.writeUpToNextSpec(sink);
+ toString(sink, f);
+ }
+
+ /// ditto
+ void toString(scope void delegate(const(char)[]) sink, ref FormatSpec!char f) const
+ {
+ immutable hex = (f.spec == 'x' || f.spec == 'X');
+ if (!(f.spec == 's' || f.spec == 'd' || f.spec =='o' || hex))
+ throw new FormatException("Format specifier not understood: %" ~ f.spec);
+
+ char[] buff;
+ if (f.spec == 'X')
+ {
+ buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.upper);
+ }
+ else if (f.spec == 'x')
+ {
+ buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.lower);
+ }
+ else if (f.spec == 'o')
+ {
+ buff = data.toOctalString();
+ }
+ else
+ {
+ buff = data.toDecimalString(0);
+ }
+ assert(buff.length > 0);
+
+ char signChar = isNegative() ? '-' : 0;
+ auto minw = buff.length + (signChar ? 1 : 0);
+
+ if (!hex && !signChar && (f.width == 0 || minw < f.width))
+ {
+ if (f.flPlus)
+ {
+ signChar = '+';
+ ++minw;
+ }
+ else if (f.flSpace)
+ {
+ signChar = ' ';
+ ++minw;
+ }
+ }
+
+ immutable maxw = minw < f.width ? f.width : minw;
+ immutable difw = maxw - minw;
+
+ if (!f.flDash && !f.flZero)
+ foreach (i; 0 .. difw)
+ sink(" ");
+
+ if (signChar)
+ sink((&signChar)[0 .. 1]);
+
+ if (!f.flDash && f.flZero)
+ foreach (i; 0 .. difw)
+ sink("0");
+
+ sink(buff);
+
+ if (f.flDash)
+ foreach (i; 0 .. difw)
+ sink(" ");
+ }
+
+ /**
+ $(D toString) is rarely directly invoked; the usual way of using it is via
+ $(REF format, std, format):
+ */
+ @system unittest
+ {
+ import std.format : format;
+
+ auto x = BigInt("1_000_000");
+ x *= 12345;
+
+ assert(format("%d", x) == "12345000000");
+ assert(format("%x", x) == "2_dfd1c040");
+ assert(format("%X", x) == "2_DFD1C040");
+ assert(format("%o", x) == "133764340100");
+ }
+
+ // Implement toHash so that BigInt works properly as an AA key.
+ /**
+ Returns: A unique hash of the BigInt's value suitable for use in a hash
+ table.
+ */
+ size_t toHash() const @safe nothrow
+ {
+ return data.toHash() + sign;
+ }
+
+ /**
+ $(D toHash) is rarely directly invoked; it is implicitly used when
+ BigInt is used as the key of an associative array.
+ */
+ @safe unittest
+ {
+ string[BigInt] aa;
+ aa[BigInt(123)] = "abc";
+ aa[BigInt(456)] = "def";
+
+ assert(aa[BigInt(123)] == "abc");
+ assert(aa[BigInt(456)] == "def");
+ }
+
+private:
+ void negate() @safe pure nothrow @nogc
+ {
+ if (!data.isZero())
+ sign = !sign;
+ }
+ bool isZero() pure const nothrow @nogc @safe
+ {
+ return data.isZero();
+ }
+ bool isNegative() pure const nothrow @nogc @safe
+ {
+ return sign;
+ }
+
+ // Generate a runtime error if division by zero occurs
+ void checkDivByZero() pure const nothrow @safe
+ {
+ if (isZero())
+ throw new Error("BigInt division by zero");
+ }
+}
+
+///
+@system unittest
+{
+ BigInt a = "9588669891916142";
+ BigInt b = "7452469135154800";
+ auto c = a * b;
+ assert(c == BigInt("71459266416693160362545788781600"));
+ auto d = b * a;
+ assert(d == BigInt("71459266416693160362545788781600"));
+ assert(d == c);
+ d = c * BigInt("794628672112");
+ assert(d == BigInt("56783581982794522489042432639320434378739200"));
+ auto e = c + d;
+ assert(e == BigInt("56783581982865981755459125799682980167520800"));
+ auto f = d + c;
+ assert(f == e);
+ auto g = f - c;
+ assert(g == d);
+ g = f - d;
+ assert(g == c);
+ e = 12345678;
+ g = c + e;
+ auto h = g / b;
+ auto i = g % b;
+ assert(h == a);
+ assert(i == e);
+ BigInt j = "-0x9A56_57f4_7B83_AB78";
+ j ^^= 11;
+}
+
+/**
+Params:
+ x = The $(D BigInt) to convert to a decimal $(D string).
+
+Returns:
+ A $(D string) that represents the $(D BigInt) as a decimal number.
+
+*/
+string toDecimalString(const(BigInt) x)
+{
+ string outbuff="";
+ void sink(const(char)[] s) { outbuff ~= s; }
+ x.toString(&sink, "%d");
+ return outbuff;
+}
+
+///
+@system unittest
+{
+ auto x = BigInt("123");
+ x *= 1000;
+ x += 456;
+
+ auto xstr = x.toDecimalString();
+ assert(xstr == "123456");
+}
+
+/**
+Params:
+ x = The $(D BigInt) to convert to a hexadecimal $(D string).
+
+Returns:
+ A $(D string) that represents the $(D BigInt) as a hexadecimal (base 16)
+ number in upper case.
+
+*/
+string toHex(const(BigInt) x)
+{
+ string outbuff="";
+ void sink(const(char)[] s) { outbuff ~= s; }
+ x.toString(&sink, "%X");
+ return outbuff;
+}
+
+///
+@system unittest
+{
+ auto x = BigInt("123");
+ x *= 1000;
+ x += 456;
+
+ auto xstr = x.toHex();
+ assert(xstr == "1E240");
+}
+
+/** Returns the absolute value of x converted to the corresponding unsigned
+type.
+
+Params:
+ x = The integral value to return the absolute value of.
+
+Returns:
+ The absolute value of x.
+
+*/
+Unsigned!T absUnsign(T)(T x)
+if (isIntegral!T)
+{
+ static if (isSigned!T)
+ {
+ import std.conv : unsigned;
+ /* This returns the correct result even when x = T.min
+ * on two's complement machines because unsigned(T.min) = |T.min|
+ * even though -T.min = T.min.
+ */
+ return unsigned((x < 0) ? cast(T)(0-x) : x);
+ }
+ else
+ {
+ return x;
+ }
+}
+
+///
+nothrow pure @system
+unittest
+{
+ assert((-1).absUnsign == 1);
+ assert(1.absUnsign == 1);
+}
+
+nothrow pure @system
+unittest
+{
+ BigInt a, b;
+ a = 1;
+ b = 2;
+ auto c = a + b;
+}
+
+nothrow pure @system
+unittest
+{
+ long a;
+ BigInt b;
+ auto c = a + b;
+ auto d = b + a;
+}
+
+nothrow pure @system
+unittest
+{
+ BigInt x = 1, y = 2;
+ assert(x < y);
+ assert(x <= y);
+ assert(y >= x);
+ assert(y > x);
+ assert(x != y);
+
+ long r1 = x.toLong;
+ assert(r1 == 1);
+
+ BigInt r2 = 10 % x;
+ assert(r2 == 0);
+
+ BigInt r3 = 10 / y;
+ assert(r3 == 5);
+
+ BigInt[] arr = [BigInt(1)];
+ auto incr = arr[0]++;
+ assert(arr == [BigInt(2)]);
+ assert(incr == BigInt(1));
+}
+
+@system unittest
+{
+ // Radix conversion
+ assert( toDecimalString(BigInt("-1_234_567_890_123_456_789"))
+ == "-1234567890123456789");
+ assert( toHex(BigInt("0x1234567890123456789")) == "123_45678901_23456789");
+ assert( toHex(BigInt("0x00000000000000000000000000000000000A234567890123456789"))
+ == "A23_45678901_23456789");
+ assert( toHex(BigInt("0x000_00_000000_000_000_000000000000_000000_")) == "0");
+
+ assert(BigInt(-0x12345678).toInt() == -0x12345678);
+ assert(BigInt(-0x12345678).toLong() == -0x12345678);
+ assert(BigInt(0x1234_5678_9ABC_5A5AL).ulongLength == 1);
+ assert(BigInt(0x1234_5678_9ABC_5A5AL).toLong() == 0x1234_5678_9ABC_5A5AL);
+ assert(BigInt(-0x1234_5678_9ABC_5A5AL).toLong() == -0x1234_5678_9ABC_5A5AL);
+ assert(BigInt(0xF234_5678_9ABC_5A5AL).toLong() == long.max);
+ assert(BigInt(-0x123456789ABCL).toInt() == -int.max);
+ char[] s1 = "123".dup; // bug 8164
+ assert(BigInt(s1) == 123);
+ char[] s2 = "0xABC".dup;
+ assert(BigInt(s2) == 2748);
+
+ assert((BigInt(-2) + BigInt(1)) == BigInt(-1));
+ BigInt a = ulong.max - 5;
+ auto b = -long.max % a;
+ assert( b == -long.max % (ulong.max - 5));
+ b = long.max / a;
+ assert( b == long.max /(ulong.max - 5));
+ assert(BigInt(1) - 1 == 0);
+ assert((-4) % BigInt(5) == -4); // bug 5928
+ assert(BigInt(-4) % BigInt(5) == -4);
+ assert(BigInt(2)/BigInt(-3) == BigInt(0)); // bug 8022
+ assert(BigInt("-1") > long.min); // bug 9548
+
+ assert(toDecimalString(BigInt("0000000000000000000000000000000000000000001234567"))
+ == "1234567");
+}
+
+@system unittest // Minimum signed value bug tests.
+{
+ assert(BigInt("-0x8000000000000000") == BigInt(long.min));
+ assert(BigInt("-0x8000000000000000")+1 > BigInt(long.min));
+ assert(BigInt("-0x80000000") == BigInt(int.min));
+ assert(BigInt("-0x80000000")+1 > BigInt(int.min));
+ assert(BigInt(long.min).toLong() == long.min); // lossy toLong bug for long.min
+ assert(BigInt(int.min).toInt() == int.min); // lossy toInt bug for int.min
+ assert(BigInt(long.min).ulongLength == 1);
+ assert(BigInt(int.min).uintLength == 1); // cast/sign extend bug in opAssign
+ BigInt a;
+ a += int.min;
+ assert(a == BigInt(int.min));
+ a = int.min - BigInt(int.min);
+ assert(a == 0);
+ a = int.min;
+ assert(a == BigInt(int.min));
+ assert(int.min % (BigInt(int.min)-1) == int.min);
+ assert((BigInt(int.min)-1)%int.min == -1);
+}
+
+@system unittest // Recursive division, bug 5568
+{
+ enum Z = 4843;
+ BigInt m = (BigInt(1) << (Z*8) ) - 1;
+ m -= (BigInt(1) << (Z*6)) - 1;
+ BigInt oldm = m;
+
+ BigInt a = (BigInt(1) << (Z*4) )-1;
+ BigInt b = m % a;
+ m /= a;
+ m *= a;
+ assert( m + b == oldm);
+
+ m = (BigInt(1) << (4846 + 4843) ) - 1;
+ a = (BigInt(1) << 4846 ) - 1;
+ b = (BigInt(1) << (4846*2 + 4843)) - 1;
+ BigInt c = (BigInt(1) << (4846*2 + 4843*2)) - 1;
+ BigInt w = c - b + a;
+ assert(w % m == 0);
+
+ // Bug 6819. ^^
+ BigInt z1 = BigInt(10)^^64;
+ BigInt w1 = BigInt(10)^^128;
+ assert(z1^^2 == w1);
+ BigInt z2 = BigInt(1)<<64;
+ BigInt w2 = BigInt(1)<<128;
+ assert(z2^^2 == w2);
+ // Bug 7993
+ BigInt n7793 = 10;
+ assert( n7793 / 1 == 10);
+ // Bug 7973
+ auto a7973 = 10_000_000_000_000_000;
+ const c7973 = 10_000_000_000_000_000;
+ immutable i7973 = 10_000_000_000_000_000;
+ BigInt v7973 = 2551700137;
+ v7973 %= a7973;
+ assert(v7973 == 2551700137);
+ v7973 %= c7973;
+ assert(v7973 == 2551700137);
+ v7973 %= i7973;
+ assert(v7973 == 2551700137);
+ // 8165
+ BigInt[2] a8165;
+ a8165[0] = a8165[1] = 1;
+}
+
+@system unittest
+{
+ import std.array;
+ import std.format;
+
+ immutable string[][] table = [
+ /* fmt, +10 -10 */
+ ["%d", "10", "-10"],
+ ["%+d", "+10", "-10"],
+ ["%-d", "10", "-10"],
+ ["%+-d", "+10", "-10"],
+
+ ["%4d", " 10", " -10"],
+ ["%+4d", " +10", " -10"],
+ ["%-4d", "10 ", "-10 "],
+ ["%+-4d", "+10 ", "-10 "],
+
+ ["%04d", "0010", "-010"],
+ ["%+04d", "+010", "-010"],
+ ["%-04d", "10 ", "-10 "],
+ ["%+-04d", "+10 ", "-10 "],
+
+ ["% 04d", " 010", "-010"],
+ ["%+ 04d", "+010", "-010"],
+ ["%- 04d", " 10 ", "-10 "],
+ ["%+- 04d", "+10 ", "-10 "],
+ ];
+
+ auto w1 = appender!(char[])();
+ auto w2 = appender!(char[])();
+
+ foreach (entry; table)
+ {
+ immutable fmt = entry[0];
+
+ formattedWrite(w1, fmt, BigInt(10));
+ formattedWrite(w2, fmt, 10);
+ assert(w1.data == w2.data);
+ assert(w1.data == entry[1]);
+ w1.clear();
+ w2.clear();
+
+ formattedWrite(w1, fmt, BigInt(-10));
+ formattedWrite(w2, fmt, -10);
+ assert(w1.data == w2.data);
+ assert(w1.data == entry[2]);
+ w1.clear();
+ w2.clear();
+ }
+}
+
+@system unittest
+{
+ import std.array;
+ import std.format;
+
+ immutable string[][] table = [
+ /* fmt, +10 -10 */
+ ["%x", "a", "-a"],
+ ["%+x", "a", "-a"],
+ ["%-x", "a", "-a"],
+ ["%+-x", "a", "-a"],
+
+ ["%4x", " a", " -a"],
+ ["%+4x", " a", " -a"],
+ ["%-4x", "a ", "-a "],
+ ["%+-4x", "a ", "-a "],
+
+ ["%04x", "000a", "-00a"],
+ ["%+04x", "000a", "-00a"],
+ ["%-04x", "a ", "-a "],
+ ["%+-04x", "a ", "-a "],
+
+ ["% 04x", "000a", "-00a"],
+ ["%+ 04x", "000a", "-00a"],
+ ["%- 04x", "a ", "-a "],
+ ["%+- 04x", "a ", "-a "],
+ ];
+
+ auto w1 = appender!(char[])();
+ auto w2 = appender!(char[])();
+
+ foreach (entry; table)
+ {
+ immutable fmt = entry[0];
+
+ formattedWrite(w1, fmt, BigInt(10));
+ formattedWrite(w2, fmt, 10);
+ assert(w1.data == w2.data); // Equal only positive BigInt
+ assert(w1.data == entry[1]);
+ w1.clear();
+ w2.clear();
+
+ formattedWrite(w1, fmt, BigInt(-10));
+ //formattedWrite(w2, fmt, -10);
+ //assert(w1.data == w2.data);
+ assert(w1.data == entry[2]);
+ w1.clear();
+ //w2.clear();
+ }
+}
+
+@system unittest
+{
+ import std.array;
+ import std.format;
+
+ immutable string[][] table = [
+ /* fmt, +10 -10 */
+ ["%X", "A", "-A"],
+ ["%+X", "A", "-A"],
+ ["%-X", "A", "-A"],
+ ["%+-X", "A", "-A"],
+
+ ["%4X", " A", " -A"],
+ ["%+4X", " A", " -A"],
+ ["%-4X", "A ", "-A "],
+ ["%+-4X", "A ", "-A "],
+
+ ["%04X", "000A", "-00A"],
+ ["%+04X", "000A", "-00A"],
+ ["%-04X", "A ", "-A "],
+ ["%+-04X", "A ", "-A "],
+
+ ["% 04X", "000A", "-00A"],
+ ["%+ 04X", "000A", "-00A"],
+ ["%- 04X", "A ", "-A "],
+ ["%+- 04X", "A ", "-A "],
+ ];
+
+ auto w1 = appender!(char[])();
+ auto w2 = appender!(char[])();
+
+ foreach (entry; table)
+ {
+ immutable fmt = entry[0];
+
+ formattedWrite(w1, fmt, BigInt(10));
+ formattedWrite(w2, fmt, 10);
+ assert(w1.data == w2.data); // Equal only positive BigInt
+ assert(w1.data == entry[1]);
+ w1.clear();
+ w2.clear();
+
+ formattedWrite(w1, fmt, BigInt(-10));
+ //formattedWrite(w2, fmt, -10);
+ //assert(w1.data == w2.data);
+ assert(w1.data == entry[2]);
+ w1.clear();
+ //w2.clear();
+ }
+}
+
+// 6448
+@system unittest
+{
+ import std.array;
+ import std.format;
+
+ auto w1 = appender!string();
+ auto w2 = appender!string();
+
+ int x = 100;
+ formattedWrite(w1, "%010d", x);
+ BigInt bx = x;
+ formattedWrite(w2, "%010d", bx);
+ assert(w1.data == w2.data);
+ //8011
+ BigInt y = -3;
+ ++y;
+ assert(y.toLong() == -2);
+ y = 1;
+ --y;
+ assert(y.toLong() == 0);
+ --y;
+ assert(y.toLong() == -1);
+ --y;
+ assert(y.toLong() == -2);
+}
+
+@safe unittest
+{
+ import std.math : abs;
+ auto r = abs(BigInt(-1000)); // 6486
+ assert(r == 1000);
+
+ auto r2 = abs(const(BigInt)(-500)); // 11188
+ assert(r2 == 500);
+ auto r3 = abs(immutable(BigInt)(-733)); // 11188
+ assert(r3 == 733);
+
+ // opCast!bool
+ BigInt one = 1, zero;
+ assert(one && !zero);
+}
+
+@system unittest // 6850
+{
+ pure long pureTest() {
+ BigInt a = 1;
+ BigInt b = 1336;
+ a += b;
+ return a.toLong();
+ }
+
+ assert(pureTest() == 1337);
+}
+
+@system unittest // 8435 & 10118
+{
+ auto i = BigInt(100);
+ auto j = BigInt(100);
+
+ // Two separate BigInt instances representing same value should have same
+ // hash.
+ assert(typeid(i).getHash(&i) == typeid(j).getHash(&j));
+ assert(typeid(i).compare(&i, &j) == 0);
+
+ // BigInt AA keys should behave consistently.
+ int[BigInt] aa;
+ aa[BigInt(123)] = 123;
+ assert(BigInt(123) in aa);
+
+ aa[BigInt(123)] = 321;
+ assert(aa[BigInt(123)] == 321);
+
+ auto keys = aa.byKey;
+ assert(keys.front == BigInt(123));
+ keys.popFront();
+ assert(keys.empty);
+}
+
+@system unittest // 11148
+{
+ void foo(BigInt) {}
+ const BigInt cbi = 3;
+ immutable BigInt ibi = 3;
+
+ assert(__traits(compiles, foo(cbi)));
+ assert(__traits(compiles, foo(ibi)));
+
+ import std.conv : to;
+ import std.meta : AliasSeq;
+
+ foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
+ {
+ foreach (T2; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
+ {
+ T1 t1 = 2;
+ T2 t2 = t1;
+
+ T2 t2_1 = to!T2(t1);
+ T2 t2_2 = cast(T2) t1;
+
+ assert(t2 == t1);
+ assert(t2 == 2);
+
+ assert(t2_1 == t1);
+ assert(t2_1 == 2);
+
+ assert(t2_2 == t1);
+ assert(t2_2 == 2);
+ }
+ }
+
+ BigInt n = 2;
+ n *= 2;
+}
+
+@safe unittest // 8167
+{
+ BigInt a = BigInt(3);
+ BigInt b = BigInt(a);
+}
+
+@safe 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);
+}
+
+@system unittest // 11600
+{
+ import std.conv;
+ import std.exception : assertThrown;
+
+ // Original bug report
+ assertThrown!ConvException(to!BigInt("avadakedavra"));
+
+ // Digit string lookalikes that are actually invalid
+ assertThrown!ConvException(to!BigInt("0123hellothere"));
+ assertThrown!ConvException(to!BigInt("-hihomarylowe"));
+ assertThrown!ConvException(to!BigInt("__reallynow__"));
+ assertThrown!ConvException(to!BigInt("-123four"));
+}
+
+@safe unittest // 11583
+{
+ BigInt x = 0;
+ assert((x > 0) == false);
+}
+
+@system unittest // 13391
+{
+ BigInt x1 = "123456789";
+ BigInt x2 = "123456789123456789";
+ BigInt x3 = "123456789123456789123456789";
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ assert((x1 * T.max) / T.max == x1);
+ assert((x2 * T.max) / T.max == x2);
+ assert((x3 * T.max) / T.max == x3);
+ }
+
+ assert(x1 / -123456789 == -1);
+ assert(x1 / 123456789U == 1);
+ assert(x1 / -123456789L == -1);
+ assert(x1 / 123456789UL == 1);
+ assert(x2 / -123456789123456789L == -1);
+ assert(x2 / 123456789123456789UL == 1);
+
+ assert(x1 / uint.max == 0);
+ assert(x1 / ulong.max == 0);
+ assert(x2 / ulong.max == 0);
+
+ x1 /= 123456789UL;
+ assert(x1 == 1);
+ x2 /= 123456789123456789UL;
+ assert(x2 == 1);
+}
+
+@system unittest // 13963
+{
+ BigInt x = 1;
+ import std.meta : AliasSeq;
+ foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint))
+ {
+ assert(is(typeof(x % Int(1)) == int));
+ }
+ assert(is(typeof(x % 1L) == long));
+ assert(is(typeof(x % 1UL) == BigInt));
+
+ auto x1 = BigInt(8);
+ auto x2 = -BigInt(long.min) + 1;
+
+ // long
+ assert(x1 % 2L == 0L);
+ assert(-x1 % 2L == 0L);
+
+ assert(x1 % 3L == 2L);
+ assert(x1 % -3L == 2L);
+ assert(-x1 % 3L == -2L);
+ assert(-x1 % -3L == -2L);
+
+ assert(x1 % 11L == 8L);
+ assert(x1 % -11L == 8L);
+ assert(-x1 % 11L == -8L);
+ assert(-x1 % -11L == -8L);
+
+ // ulong
+ assert(x1 % 2UL == BigInt(0));
+ assert(-x1 % 2UL == BigInt(0));
+
+ assert(x1 % 3UL == BigInt(2));
+ assert(-x1 % 3UL == -BigInt(2));
+
+ assert(x1 % 11UL == BigInt(8));
+ assert(-x1 % 11UL == -BigInt(8));
+
+ assert(x2 % ulong.max == x2);
+ assert(-x2 % ulong.max == -x2);
+}
+
+@system unittest // 14124
+{
+ auto x = BigInt(-3);
+ x %= 3;
+ assert(!x.isNegative());
+ assert(x.isZero());
+
+ x = BigInt(-3);
+ x %= cast(ushort) 3;
+ assert(!x.isNegative());
+ assert(x.isZero());
+
+ x = BigInt(-3);
+ x %= 3L;
+ assert(!x.isNegative());
+ assert(x.isZero());
+
+ x = BigInt(3);
+ x %= -3;
+ assert(!x.isNegative());
+ assert(x.isZero());
+}
+
+// issue 15678
+@system unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!ConvException(BigInt(""));
+ assertThrown!ConvException(BigInt("0x1234BARF"));
+ assertThrown!ConvException(BigInt("1234PUKE"));
+}
+
+// Issue 6447
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ auto s = BigInt(1_000_000_000_000);
+ auto e = BigInt(1_000_000_000_003);
+ auto r = iota(s, e);
+ assert(r.equal([
+ BigInt(1_000_000_000_000),
+ BigInt(1_000_000_000_001),
+ BigInt(1_000_000_000_002)
+ ]));
+}
+
+// Issue 17330
+@system unittest
+{
+ auto b = immutable BigInt("123");
+}