/** 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 $(WEB gmplib.org, GMP library) instead. * * License: 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; private import std.internal.math.biguintcore; private import std.format : FormatSpec, FormatException; 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. * * 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). Example: ---------------------------------------------------- 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; ---------------------------------------------------- * */ 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 D decimal or hex literal: /// It may have a leading + or - sign; followed by "0x" if hexadecimal. /// Underscores are permitted. /// BUG: Should throw a IllegalArgumentException/ConvError if invalid character found this(T : const(char)[] )(T s) { bool neg = false; if (s[0] == '-') { neg = true; s = s[1..$]; } else if (s[0] == '+') { s = s[1..$]; } data = 0UL; auto q = 0X3; bool ok; assert(isZero()); if (s.length > 2 && (s[0..2] == "0x" || s[0..2] == "0X")) { ok = data.fromHexString(s[2..$]); } else { ok = data.fromDecimalString(s); } assert(ok); if (isZero()) neg = false; sign = neg; } /// this(T)(T x) if (isIntegral!T) { data = data.init; // @@@: Workaround for compiler bug opAssign(x); } /// BigInt opAssign(T)(T x) if (isIntegral!T) { data = cast(ulong)((x < 0) ? -x : x); sign = (x < 0); return this; } /// BigInt opAssign(T:BigInt)(T x) { data = x.data; sign = x.sign; return this; } // BigInt op= integer BigInt opOpAssign(string op, T)(T y) if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" || op==">>" || op=="<<" || op=="^^") && isIntegral!T) { ulong u = cast(ulong)(y < 0 ? -y : 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 assert(!is(T == long) && !is(T == ulong)); data = BigUint.divInt(data, cast(uint)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); } // 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 assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported"); return this; } // BigInt op= BigInt BigInt opOpAssign(string op, T)(T y) if ((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 assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported"); return this; } // BigInt op BigInt BigInt opBinary(string op, T)(T y) if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="%") && is (T: BigInt)) { BigInt r = this; return r.opOpAssign!(op)(y); } // BigInt op integer BigInt opBinary(string op, T)(T y) if ((op=="+" || op == "*" || op=="-" || op=="/" || op==">>" || op=="<<" || op=="^^") && isIntegral!T) { BigInt r = this; return r.opOpAssign!(op)(y); } // int opBinary(string op, T : int)(T y) if (op == "%" && isIntegral!T) { assert(y!=0); uint u = y < 0 ? -y : 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; } // Commutative operators BigInt opBinaryRight(string op, T)(T y) if ((op=="+" || op=="*") && isIntegral!T) { return opBinary!(op)(y); } // BigInt = integer op BigInt BigInt opBinaryRight(string op, T)(T y) if (op == "-" && isIntegral!T) { ulong u = cast(ulong)(y < 0 ? -y : 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 T opBinaryRight(string op, T)(T x) if ((op=="%" || op=="/") && isIntegral!T) { static if (op == "%") { checkDivByZero(); // x%y always has the same sign as x. if (data.ulongLength() > 1) return x; ulong u = x < 0 ? -x : x; ulong 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 == "/") { checkDivByZero(); if (data.ulongLength() > 1) return 0; return cast(T)(x / data.peekUlong(0)); } } // const unary operations BigInt opUnary(string op)() /*const*/ if (op=="+" || op=="-") { static if (op=="-") { BigInt r = this; r.negate(); return r; } else static if (op=="+") return this; } // non-const unary operations BigInt opUnary(string op)() 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; } } /// bool opEquals()(auto ref const BigInt y) const { return sign == y.sign && y.data == data; } /// bool opEquals(T)(T y) const if (isIntegral!T) { if (sign != (y<0)) return 0; return data.opEquals(cast(ulong)( y>=0 ? y : -y)); } /// int opCmp(T)(T y) if (isIntegral!T) { if (sign != (y<0) ) return sign ? -1 : 1; int cmp = data.opCmp(cast(ulong)(y >= 0 ? y : -y)); return sign? -cmp: cmp; } /// int opCmp(T:BigInt)(T y) { if (sign!=y.sign) return sign ? -1 : 1; int cmp = data.opCmp(y.data); return sign? -cmp: cmp; } /// Returns the value of this BigInt as a long, /// or +- long.max if outside the representable range. long toLong() pure const { return (sign ? -1 : 1) * (data.ulongLength() == 1 && (data.peekUlong(0) <= cast(ulong)(long.max)) ? cast(long)(data.peekUlong(0)) : long.max); } /// Returns the value of this BigInt as an int, /// or +- int.max if outside the representable range. int toInt() pure const { return (sign ? -1 : 1) * (data.uintLength() == 1 && (data.peekUint(0) <= cast(uint)(int.max)) ? cast(int)(data.peekUint(0)) : int.max); } /// Number of significant uints which are used in storing this number. /// The absolute value of this BigInt is always < 2^^(32*uintLength) @property size_t uintLength() pure const { return data.uintLength(); } /// Number of significant ulongs which are used in storing this number. /// The absolute value of this BigInt is always < 2^^(64*ulongLength) @property size_t ulongLength() pure const { return data.ulongLength(); } /** Convert the BigInt to string, passing it to 'sink'. * * $(TABLE The output format is controlled via formatString: * $(TR $(TD "d") $(TD Decimal)) * $(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); } void toString(scope void delegate(const(char)[]) sink, ref FormatSpec!char f) const { auto hex = (f.spec == 'x' || f.spec == 'X'); if (!(f.spec == 's' || f.spec == 'd' || hex)) throw new FormatException("Format specifier not understood: %" ~ f.spec); char[] buff = hex ? data.toHexString(0, '_', 0, f.flZero ? '0' : ' ') : 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; } auto maxw = minw < f.width ? f.width : minw; auto 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(" "); } /+ private: /// Convert to a hexadecimal string, with an underscore every /// 8 characters. string toHex() { string buff = data.toHexString(1, '_'); if (isNegative()) buff[0] = '-'; else buff = buff[1..$]; return buff; } +/ private: void negate() { if (!data.isZero()) sign = !sign; } bool isZero() pure const { return data.isZero(); } bool isNegative() pure const { return sign; } // Generate a runtime error if division by zero occurs void checkDivByZero() pure const { if (isZero()) throw new Error("BigInt division by zero"); } } string toDecimalString(BigInt x) { string outbuff=""; void sink(const(char)[] s) { outbuff ~= s; } x.toString(&sink, "%d"); return outbuff; } string toHex(BigInt x) { string outbuff=""; void sink(const(char)[] s) { outbuff ~= s; } x.toString(&sink, "%x"); return outbuff; } 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 } 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; } 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(); } } 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 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); }