mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 13:10:35 +03:00
2417 lines
71 KiB
D
2417 lines
71 KiB
D
/** 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.spec : FormatSpec;
|
|
import std.format : FormatException;
|
|
import std.internal.math.biguintcore;
|
|
import std.internal.math.biguintnoasm : BigDigit;
|
|
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 &&
|
|
!isNarrowString!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 (isNarrowString!Range)
|
|
{
|
|
import std.utf : byCodeUnit;
|
|
this(s.byCodeUnit);
|
|
}
|
|
|
|
@safe 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 sign and a magnitude.
|
|
*
|
|
* The magnitude is an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
|
|
* of unsigned integers that satisfies either $(REF hasLength, std,range,primitives)
|
|
* or $(REF isForwardRange, std,range,primitives). The first (leftmost)
|
|
* element of the magnitude is considered the most significant.
|
|
*
|
|
* Params:
|
|
* isNegative = true for negative, false for non-negative
|
|
* (ignored when magnitude is zero)
|
|
* magnitude = a finite range of unsigned integers
|
|
*/
|
|
this(Range)(bool isNegative, Range magnitude)
|
|
if (isInputRange!Range &&
|
|
isUnsigned!(ElementType!Range) &&
|
|
(hasLength!Range || isForwardRange!Range) &&
|
|
!isInfinite!Range)
|
|
{
|
|
data.fromMagnitude(magnitude);
|
|
sign = isNegative && !data.isZero;
|
|
}
|
|
|
|
///
|
|
pure @safe unittest
|
|
{
|
|
ubyte[] magnitude = [1, 2, 3, 4, 5, 6];
|
|
auto b1 = BigInt(false, magnitude);
|
|
assert(cast(long) b1 == 0x01_02_03_04_05_06L);
|
|
auto b2 = BigInt(true, magnitude);
|
|
assert(cast(long) b2 == -0x01_02_03_04_05_06L);
|
|
}
|
|
|
|
/// Construct a `BigInt` from a built-in integral type.
|
|
this(T)(T x) pure nothrow @safe
|
|
if (isIntegral!T)
|
|
{
|
|
data = data.init; // @@@: Workaround for compiler bug
|
|
opAssign(x);
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
ulong data = 1_000_000_000_000;
|
|
auto bigData = BigInt(data);
|
|
assert(bigData == BigInt("1_000_000_000_000"));
|
|
}
|
|
|
|
/// Construct a `BigInt` from another `BigInt`.
|
|
this(T)(T x) pure nothrow @safe
|
|
if (is(immutable T == immutable BigInt))
|
|
{
|
|
opAssign(x);
|
|
}
|
|
|
|
///
|
|
@safe 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 @safe
|
|
if (isIntegral!T)
|
|
{
|
|
data = cast(ulong) absUnsign(x);
|
|
sign = (x < 0);
|
|
return this;
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
auto b = BigInt("123");
|
|
b = 456;
|
|
assert(b == BigInt("456"));
|
|
}
|
|
|
|
/// Assignment from another BigInt.
|
|
BigInt opAssign(T:BigInt)(T x) pure @nogc @safe
|
|
{
|
|
data = x.data;
|
|
sign = x.sign;
|
|
return this;
|
|
}
|
|
|
|
///
|
|
@safe 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
|
|
* `BigInt op= integer`.
|
|
*/
|
|
BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope
|
|
if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
|
|
|| op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T)
|
|
{
|
|
ulong u = absUnsign(y);
|
|
|
|
static if (op=="+")
|
|
{
|
|
data = BigUint.addOrSubInt!ulong(data, u, wantSub: sign != (y<0), sign);
|
|
}
|
|
else static if (op=="-")
|
|
{
|
|
data = BigUint.addOrSubInt!ulong(data, u, wantSub: sign == (y<0), sign);
|
|
}
|
|
else static if (op=="*")
|
|
{
|
|
if (y == 0 || data.isZero())
|
|
{
|
|
sign = false;
|
|
data = 0UL;
|
|
return this;
|
|
}
|
|
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.opBinary!(op)(u);
|
|
}
|
|
else
|
|
{
|
|
data = data.opBinary!(op)(u);
|
|
if (data.isZero())
|
|
sign = false;
|
|
}
|
|
}
|
|
else static if (op=="^^")
|
|
{
|
|
sign = (y & 1) ? sign : false;
|
|
if (y < 0)
|
|
{
|
|
checkDivByZero();
|
|
data = cast(ulong) (data == 1);
|
|
}
|
|
else
|
|
{
|
|
data = BigUint.pow(data, u);
|
|
}
|
|
}
|
|
else static if (op=="&")
|
|
{
|
|
if (y >= 0 && (y <= 1 || !sign)) // In these cases we can avoid some allocation.
|
|
{
|
|
static if (T.sizeof <= uint.sizeof && BigDigit.sizeof <= uint.sizeof)
|
|
data = cast(ulong) data.peekUint(0) & y;
|
|
else
|
|
data = data.peekUlong(0) & y;
|
|
sign = false;
|
|
}
|
|
else
|
|
{
|
|
BigInt b = y;
|
|
opOpAssign!op(b);
|
|
}
|
|
}
|
|
else static if (op=="|" || op=="^")
|
|
{
|
|
BigInt b = y;
|
|
opOpAssign!op(b);
|
|
}
|
|
else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported");
|
|
return this;
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=10565
|
|
@safe unittest
|
|
{
|
|
// Test cases from the issue
|
|
BigInt a = BigInt("0");
|
|
BigInt b = BigInt("-0");
|
|
BigInt c = BigInt("0") * -1;
|
|
BigInt d = BigInt("0") * -42;
|
|
BigInt e = BigInt("0"); e *= -1;
|
|
BigInt f = BigInt(c);
|
|
BigInt g = BigInt("0") * cast(byte) -1;
|
|
BigInt h = BigInt("0"); h *= BigInt("-1");
|
|
BigInt i = BigInt("0"); i -= 2 * i;
|
|
BigInt j = BigInt("0"); j = -j;
|
|
// All of these should be zero and not negative
|
|
auto values = [a, b, c, d, e, f, g, h, i, j];
|
|
foreach (val; values)
|
|
{
|
|
assert(val == 0, "BigInt value should be equal to zero");
|
|
assert(!(val < 0), "BigInt zero should not be negative");
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
auto b = BigInt("1_000_000_000");
|
|
|
|
b += 12345;
|
|
assert(b == BigInt("1_000_012_345"));
|
|
|
|
b /= 5;
|
|
assert(b == BigInt("200_002_469"));
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=16264
|
|
@safe unittest
|
|
{
|
|
auto a = BigInt(
|
|
`335690982744637013564796917901053301979460129353374296317539383938630086938` ~
|
|
`465898213033510992292836631752875403891802201862860531801760096359705447768` ~
|
|
`957432600293361240407059207520920532482429912948952142341440301429494694368` ~
|
|
`264560802292927144211230021750155988283029753927847924288850436812178022006` ~
|
|
`408597793414273953252832688620479083497367463977081627995406363446761896298` ~
|
|
`967177607401918269561385622811274398143647535024987050366350585544531063531` ~
|
|
`7118554808325723941557169427279911052268935775`);
|
|
|
|
auto b = BigInt(
|
|
`207672245542926038535480439528441949928508406405023044025560363701392340829` ~
|
|
`852529131306106648201340460604257466180580583656068555417076345439694125326` ~
|
|
`843947164365500055567495554645796102453565953360564114634705366335703491527` ~
|
|
`429426780005741168078089657359833601261803592920462081364401456331489106355` ~
|
|
`199133982282631108670436696758342051198891939367812305559960349479160308314` ~
|
|
`068518200681530999860641597181672463704794566473241690395901768680673716414` ~
|
|
`243691584391572899147223065906633310537507956952626106509069491302359792769` ~
|
|
`378934570685117202046921464019396759638376362935855896435623442486036961070` ~
|
|
`534574698959398017332214518246531363445309522357827985468581166065335726996` ~
|
|
`711467464306784543112544076165391268106101754253962102479935962248302404638` ~
|
|
`21737237102628470475027851189594709504`);
|
|
|
|
BigInt c = a * b; // Crashes
|
|
|
|
assert(c == BigInt(
|
|
`697137001950904057507249234183127244116872349433141878383548259425589716813` ~
|
|
`135440660252012378417669596912108637127036044977634382385990472429604619344` ~
|
|
`738746224291111527200379708978133071390303850450970292020176369525401803474` ~
|
|
`998613408923490273129022167907826017408385746675184651576154302536663744109` ~
|
|
`111018961065316024005076097634601030334948684412785487182572502394847587887` ~
|
|
`507385831062796361152176364659197432600147716058873232435238712648552844428` ~
|
|
`058885217631715287816333209463171932255049134340904981280717725999710525214` ~
|
|
`161541960645335744430049558161514565159449390036287489478108344584188898872` ~
|
|
`434914159748515512161981956372737022393466624249130107254611846175580584736` ~
|
|
`276213025837422102290580044755202968610542057651282410252208599309841499843` ~
|
|
`672251048622223867183370008181364966502137725166782667358559333222947265344` ~
|
|
`524195551978394625568228658697170315141077913403482061673401937141405425042` ~
|
|
`283546509102861986303306729882186190883772633960389974665467972016939172303` ~
|
|
`653623175801495207204880400522581834672918935651426160175413277309985678579` ~
|
|
`830872397214091472424064274864210953551447463312267310436493480881235642109` ~
|
|
`668498742629676513172286703948381906930297135997498416573231570483993847269` ~
|
|
`479552708416124555462530834668011570929850407031109157206202741051573633443` ~
|
|
`58105600`
|
|
));
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=24028
|
|
@system unittest
|
|
{
|
|
import std.exception : assertThrown;
|
|
import core.exception : AssertError;
|
|
|
|
assert(BigInt(100) ^^ -1 == BigInt(0));
|
|
assert(BigInt(1) ^^ -1 == BigInt(1));
|
|
assert(BigInt(-1) ^^ -1 == BigInt(-1));
|
|
assert(BigInt(-1) ^^ -2 == BigInt(1));
|
|
assertThrown!AssertError(BigInt(0) ^^ -1);
|
|
}
|
|
|
|
/**
|
|
* Implements assignment operators of the form `BigInt op= BigInt`.
|
|
*/
|
|
BigInt opOpAssign(string op, T)(T y) pure nothrow @safe return scope
|
|
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;
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
auto x = BigInt("123");
|
|
auto y = BigInt("321");
|
|
x += y;
|
|
assert(x == BigInt("444"));
|
|
}
|
|
|
|
/**
|
|
* Implements binary operators between `BigInt`s.
|
|
*/
|
|
BigInt opBinary(string op, T)(T y) pure nothrow @safe const return scope
|
|
if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" ||
|
|
op=="/" || op=="%") && is (T: BigInt))
|
|
{
|
|
BigInt r = this;
|
|
return r.opOpAssign!(op)(y);
|
|
}
|
|
|
|
///
|
|
@safe 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 @safe const return scope
|
|
if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" ||
|
|
op=="^"|| op==">>" || op=="<<" || op=="^^")
|
|
&& isIntegral!T)
|
|
{
|
|
BigInt r = this;
|
|
r.opOpAssign!(op)(y);
|
|
return r;
|
|
}
|
|
|
|
///
|
|
@safe 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 `uint`) $(TD $(RARR)) $(TD `long`))
|
|
$(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 @safe const
|
|
if (op == "%" && isIntegral!T)
|
|
{
|
|
assert(y != 0, "% 0 not allowed");
|
|
|
|
// BigInt % uint => long
|
|
// BigInt % long => long
|
|
// BigInt % ulong => BigInt
|
|
// BigInt % other_type => int
|
|
static if (is(immutable T == immutable long) || is(immutable T == immutable ulong))
|
|
{
|
|
auto r = this % BigInt(y);
|
|
|
|
static if (is(immutable T == immutable long))
|
|
{
|
|
return r.toLong();
|
|
}
|
|
else
|
|
{
|
|
// return as-is to avoid overflow
|
|
return r;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
immutable uint u = absUnsign(y);
|
|
static if (is(immutable T == immutable uint))
|
|
alias R = long;
|
|
else
|
|
alias R = int;
|
|
R 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;
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe 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 @safe const
|
|
if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T)
|
|
{
|
|
return opBinary!(op)(y);
|
|
}
|
|
|
|
///
|
|
@safe 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 @safe const
|
|
if (op == "-" && isIntegral!T)
|
|
{
|
|
ulong u = absUnsign(y);
|
|
BigInt r;
|
|
static if (op == "-")
|
|
{
|
|
r.sign = sign;
|
|
r.data = BigUint.addOrSubInt!ulong(data, u, wantSub: sign == (y<0), r.sign);
|
|
r.negate();
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// integer = integer op BigInt
|
|
/// ditto
|
|
T opBinaryRight(string op, T)(T x) pure nothrow @safe 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 @safe 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 @safe
|
|
if (op=="++" || op=="--")
|
|
{
|
|
static if (op=="++")
|
|
{
|
|
data = BigUint.addOrSubInt!ulong(data, 1UL, wantSub: sign, sign);
|
|
return this;
|
|
}
|
|
else static if (op=="--")
|
|
{
|
|
data = BigUint.addOrSubInt!ulong(data, 1UL, wantSub: !sign, sign);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe 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
|
|
numeric types.
|
|
*/
|
|
bool opEquals()(auto ref const BigInt y) const pure @nogc @safe
|
|
{
|
|
return sign == y.sign && y.data == data;
|
|
}
|
|
|
|
/// ditto
|
|
bool opEquals(T)(const T y) const pure nothrow @nogc @safe
|
|
if (isIntegral!T)
|
|
{
|
|
if (sign != (y<0))
|
|
return 0;
|
|
return data.opEquals(cast(ulong) absUnsign(y));
|
|
}
|
|
|
|
/// ditto
|
|
bool opEquals(T)(const T y) const pure nothrow @nogc
|
|
if (isFloatingPoint!T)
|
|
{
|
|
return 0 == opCmp(y);
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
// Note that when comparing a BigInt to a float or double the
|
|
// full precision of the BigInt is always considered, unlike
|
|
// when comparing an int to a float or a long to a double.
|
|
assert(BigInt(123456789) != cast(float) 123456789);
|
|
}
|
|
|
|
@safe 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);
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
import std.math.operations : nextDown, nextUp;
|
|
|
|
const x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
|
|
BigInt x1 = x + 1;
|
|
BigInt x2 = x - 1;
|
|
|
|
const d = 0x1.abcde8p124;
|
|
assert(x == d);
|
|
assert(x1 != d);
|
|
assert(x2 != d);
|
|
assert(x != nextUp(d));
|
|
assert(x != nextDown(d));
|
|
assert(x != double.nan);
|
|
|
|
const dL = 0x1.abcde8p124L;
|
|
assert(x == dL);
|
|
assert(x1 != dL);
|
|
assert(x2 != dL);
|
|
assert(x != nextUp(dL));
|
|
assert(x != nextDown(dL));
|
|
assert(x != real.nan);
|
|
|
|
assert(BigInt(0) == 0.0f);
|
|
assert(BigInt(0) == 0.0);
|
|
assert(BigInt(0) == 0.0L);
|
|
assert(BigInt(0) == -0.0f);
|
|
assert(BigInt(0) == -0.0);
|
|
assert(BigInt(0) == -0.0L);
|
|
assert(BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") != float.infinity);
|
|
}
|
|
|
|
/**
|
|
Implements casting to `bool`.
|
|
*/
|
|
T opCast(T:bool)() pure nothrow @nogc @safe const
|
|
{
|
|
return !isZero();
|
|
}
|
|
|
|
///
|
|
@safe 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 @safe 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(%s) cannot be represented as a %s"
|
|
.format(this.toDecimalString, T.stringof));
|
|
}
|
|
|
|
///
|
|
@safe 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);
|
|
}
|
|
|
|
@safe 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 floating point types.
|
|
*/
|
|
T opCast(T)() @safe nothrow @nogc const
|
|
if (isFloatingPoint!T)
|
|
{
|
|
return toFloat!(T, "nearest");
|
|
}
|
|
|
|
///
|
|
@system unittest
|
|
{
|
|
assert(cast(float) BigInt("35540592535949172786332045140593475584")
|
|
== 35540592535949172786332045140593475584.0f);
|
|
assert(cast(double) BigInt("35540601499647381470685035515422441472")
|
|
== 35540601499647381470685035515422441472.0);
|
|
assert(cast(real) BigInt("35540601499647381470685035515422441472")
|
|
== 35540601499647381470685035515422441472.0L);
|
|
|
|
assert(cast(float) BigInt("-0x1345_6780_0000_0000_0000_0000_0000") == -0x1.3456_78p+108f );
|
|
assert(cast(double) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108 );
|
|
assert(cast(real) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108L);
|
|
}
|
|
|
|
/// Rounding when casting to floating point
|
|
@system unittest
|
|
{
|
|
// BigInts whose values cannot be exactly represented as float/double/real
|
|
// are rounded when cast to float/double/real. When cast to float or
|
|
// double or 64-bit real the rounding is strictly defined. When cast
|
|
// to extended-precision real the rounding rules vary by environment.
|
|
|
|
// BigInts that fall somewhere between two non-infinite floats/doubles
|
|
// are rounded to the closer value when cast to float/double.
|
|
assert(cast(float) BigInt(0x1aaa_aae7) == 0x1.aaa_aaep+28f);
|
|
assert(cast(float) BigInt(0x1aaa_aaff) == 0x1.aaa_ab0p+28f);
|
|
assert(cast(float) BigInt(-0x1aaa_aae7) == -0x1.aaaaaep+28f);
|
|
assert(cast(float) BigInt(-0x1aaa_aaff) == -0x1.aaaab0p+28f);
|
|
|
|
assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa77) == 0x1.aaa_aaaa_aaaa_aa00p+60);
|
|
assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aaff) == 0x1.aaa_aaaa_aaaa_ab00p+60);
|
|
assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa77) == -0x1.aaa_aaaa_aaaa_aa00p+60);
|
|
assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aaff) == -0x1.aaa_aaaa_aaaa_ab00p+60);
|
|
|
|
// BigInts that fall exactly between two non-infinite floats/doubles
|
|
// are rounded away from zero when cast to float/double. (Note that
|
|
// in most environments this is NOT the same rounding rule rule used
|
|
// when casting int/long to float/double.)
|
|
assert(cast(float) BigInt(0x1aaa_aaf0) == 0x1.aaa_ab0p+28f);
|
|
assert(cast(float) BigInt(-0x1aaa_aaf0) == -0x1.aaaab0p+28f);
|
|
|
|
assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa80) == 0x1.aaa_aaaa_aaaa_ab00p+60);
|
|
assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa80) == -0x1.aaa_aaaa_aaaa_ab00p+60);
|
|
|
|
// BigInts that are bounded on one side by the largest positive or
|
|
// most negative finite float/double and on the other side by infinity
|
|
// or -infinity are rounded as if in place of infinity was the value
|
|
// `2^^(T.max_exp)` when cast to float/double.
|
|
assert(cast(float) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") == float.infinity);
|
|
assert(cast(float) BigInt("-999_999_999_999_999_999_999_999_999_999_999_999_999") == -float.infinity);
|
|
|
|
assert(cast(double) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < double.infinity);
|
|
assert(cast(real) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < real.infinity);
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
// Test exponent overflow is correct.
|
|
assert(cast(float) BigInt(0x1fffffff) == 0x1.000000p+29f);
|
|
assert(cast(double) BigInt(0x1fff_ffff_ffff_fff0) == 0x1.000000p+61);
|
|
}
|
|
|
|
private T toFloat(T, string roundingMode)() @safe nothrow @nogc const
|
|
if (__traits(isFloating, T) && (roundingMode == "nearest" || roundingMode == "truncate"))
|
|
{
|
|
import core.bitop : bsr;
|
|
enum performRounding = (roundingMode == "nearest");
|
|
enum performTruncation = (roundingMode == "truncate");
|
|
static assert(performRounding || performTruncation, "unrecognized rounding mode");
|
|
enum int totalNeededBits = T.mant_dig + int(performRounding);
|
|
static if (totalNeededBits <= 64)
|
|
{
|
|
// We need to examine the top two 64-bit words, not just the top one,
|
|
// since the top word could have just a single significant bit.
|
|
const ulongLength = data.ulongLength;
|
|
const ulong w1 = data.peekUlong(ulongLength - 1);
|
|
if (w1 == 0)
|
|
return T(0); // Special: exponent should be all zero bits, plus bsr(w1) is undefined.
|
|
const ulong w2 = ulongLength < 2 ? 0 : data.peekUlong(ulongLength - 2);
|
|
const uint w1BitCount = bsr(w1) + 1;
|
|
ulong sansExponent = (w1 << (64 - w1BitCount)) | (w2 >>> (w1BitCount));
|
|
size_t exponent = (ulongLength - 1) * 64 + w1BitCount + 1;
|
|
static if (performRounding)
|
|
{
|
|
sansExponent += 1UL << (64 - totalNeededBits);
|
|
if (0 <= cast(long) sansExponent) // Use high bit to detect overflow.
|
|
{
|
|
// Do not bother filling in the high bit of sansExponent
|
|
// with 1. It will be discarded by float and double and 80
|
|
// bit real cannot be on this path with rounding enabled.
|
|
exponent += 1;
|
|
}
|
|
}
|
|
static if (T.mant_dig == float.mant_dig)
|
|
{
|
|
if (exponent >= T.max_exp)
|
|
return isNegative ? -T.infinity : T.infinity;
|
|
uint resultBits = (uint(isNegative) << 31) | // sign bit
|
|
((0xFF & (exponent - float.min_exp)) << 23) | // exponent
|
|
cast(uint) ((sansExponent << 1) >>> (64 - 23)); // mantissa.
|
|
// TODO: remove @trusted lambda after DIP 1000 is enabled by default.
|
|
return (() @trusted => *cast(float*) &resultBits)();
|
|
}
|
|
else static if (T.mant_dig == double.mant_dig)
|
|
{
|
|
if (exponent >= T.max_exp)
|
|
return isNegative ? -T.infinity : T.infinity;
|
|
ulong resultBits = (ulong(isNegative) << 63) | // sign bit
|
|
((0x7FFUL & (exponent - double.min_exp)) << 52) | // exponent
|
|
((sansExponent << 1) >>> (64 - 52)); // mantissa.
|
|
// TODO: remove @trusted lambda after DIP 1000 is enabled by default.
|
|
return (() @trusted => *cast(double*) &resultBits)();
|
|
}
|
|
else
|
|
{
|
|
import core.math : ldexp;
|
|
return ldexp(isNegative ? -cast(real) sansExponent : cast(real) sansExponent,
|
|
cast(int) exponent - 65);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
import core.math : ldexp;
|
|
const ulongLength = data.ulongLength;
|
|
if ((ulongLength - 1) * 64L > int.max)
|
|
return isNegative ? -T.infinity : T.infinity;
|
|
int scale = cast(int) ((ulongLength - 1) * 64);
|
|
const ulong w1 = data.peekUlong(ulongLength - 1);
|
|
if (w1 == 0)
|
|
return T(0); // Special: bsr(w1) is undefined.
|
|
int bitsStillNeeded = totalNeededBits - bsr(w1) - 1;
|
|
T acc = ldexp(cast(T) w1, scale);
|
|
for (ptrdiff_t i = ulongLength - 2; i >= 0 && bitsStillNeeded > 0; i--)
|
|
{
|
|
ulong w = data.peekUlong(i);
|
|
// To round towards zero we must make sure not to use too many bits.
|
|
if (bitsStillNeeded >= 64)
|
|
{
|
|
acc += ldexp(cast(T) w, scale -= 64);
|
|
bitsStillNeeded -= 64;
|
|
}
|
|
else
|
|
{
|
|
w = (w >>> (64 - bitsStillNeeded)) << (64 - bitsStillNeeded);
|
|
acc += ldexp(cast(T) w, scale -= 64);
|
|
break;
|
|
}
|
|
}
|
|
if (isNegative)
|
|
acc = -acc;
|
|
return cast(T) acc;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Implements casting to/from qualified `BigInt`'s.
|
|
|
|
Warning: Casting to/from `const` or `immutable` may break type
|
|
system guarantees. Use with care.
|
|
*/
|
|
T opCast(T)() pure nothrow @nogc const
|
|
if (is(immutable T == immutable BigInt))
|
|
{
|
|
return this;
|
|
}
|
|
|
|
///
|
|
@safe 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 numeric types.
|
|
*/
|
|
int opCmp(ref const BigInt y) pure nothrow @nogc @safe const
|
|
{
|
|
// Simply redirect to the "real" opCmp implementation.
|
|
return this.opCmp!BigInt(y);
|
|
}
|
|
|
|
/// ditto
|
|
int opCmp(T)(const T y) pure nothrow @nogc @safe 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)(const T y) nothrow @nogc @safe const
|
|
if (isFloatingPoint!T)
|
|
{
|
|
import core.bitop : bsr;
|
|
import std.math.operations : cmp;
|
|
import std.math.traits : isFinite;
|
|
|
|
const asFloat = toFloat!(T, "truncate");
|
|
if (asFloat != y)
|
|
return cmp(asFloat, y); // handles +/- NaN.
|
|
if (!isFinite(y))
|
|
return isNegative ? 1 : -1;
|
|
const ulongLength = data.ulongLength;
|
|
const w1 = data.peekUlong(ulongLength - 1);
|
|
if (w1 == 0)
|
|
return 0; // Special: bsr(w1) is undefined.
|
|
const numSignificantBits = (ulongLength - 1) * 64 + bsr(w1) + 1;
|
|
for (ptrdiff_t bitsRemainingToCheck = numSignificantBits - T.mant_dig, i = 0;
|
|
bitsRemainingToCheck > 0; i++, bitsRemainingToCheck -= 64)
|
|
{
|
|
auto word = data.peekUlong(i);
|
|
if (word == 0)
|
|
continue;
|
|
// Make sure we're only checking digits that are beyond
|
|
// the precision of `y`.
|
|
if (bitsRemainingToCheck < 64 && (word << (64 - bitsRemainingToCheck)) == 0)
|
|
break; // This can only happen on the last loop iteration.
|
|
return isNegative ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
/// ditto
|
|
int opCmp(T:BigInt)(const T y) pure nothrow @nogc @safe const
|
|
{
|
|
if (sign != y.sign)
|
|
return sign ? -1 : 1;
|
|
immutable cmp = data.opCmp(y.data);
|
|
return sign? -cmp: cmp;
|
|
}
|
|
|
|
///
|
|
@safe 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);
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
|
|
BigInt y = x - 1;
|
|
BigInt z = x + 1;
|
|
|
|
double d = 0x1.abcde8p124;
|
|
assert(y < d);
|
|
assert(z > d);
|
|
assert(x >= d && x <= d);
|
|
|
|
// Note that when comparing a BigInt to a float or double the
|
|
// full precision of the BigInt is always considered, unlike
|
|
// when comparing an int to a float or a long to a double.
|
|
assert(BigInt(123456789) < cast(float) 123456789);
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
assert(BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < float.infinity);
|
|
|
|
// Test `real` works.
|
|
auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
|
|
BigInt y = x - 1;
|
|
BigInt z = x + 1;
|
|
|
|
real d = 0x1.abcde8p124;
|
|
assert(y < d);
|
|
assert(z > d);
|
|
assert(x >= d && x <= d);
|
|
|
|
// Test comparison for numbers of 64 bits or fewer.
|
|
auto w1 = BigInt(0x1abc_de80_0000_0000);
|
|
auto w2 = w1 - 1;
|
|
auto w3 = w1 + 1;
|
|
assert(w1.ulongLength == 1);
|
|
assert(w2.ulongLength == 1);
|
|
assert(w3.ulongLength == 1);
|
|
|
|
double e = 0x1.abcde8p+60;
|
|
assert(w1 >= e && w1 <= e);
|
|
assert(w2 < e);
|
|
assert(w3 > e);
|
|
|
|
real eL = 0x1.abcde8p+60;
|
|
assert(w1 >= eL && w1 <= eL);
|
|
assert(w2 < eL);
|
|
assert(w3 > eL);
|
|
}
|
|
|
|
/**
|
|
Returns: The value of this `BigInt` as a `long`, or `long.max`/`long.min`
|
|
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);
|
|
}
|
|
|
|
///
|
|
@safe unittest
|
|
{
|
|
auto b = BigInt("12345");
|
|
long l = b.toLong();
|
|
assert(l == 12345);
|
|
}
|
|
|
|
/**
|
|
Returns: The value of this `BigInt` as an `int`, or `int.max`/`int.min` 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);
|
|
}
|
|
|
|
///
|
|
@safe 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 `uint`s which are used in storing this number.
|
|
/// The absolute value of this `BigInt` is always < 2$(SUPERSCRIPT 32*uintLength)
|
|
@property size_t uintLength() @safe pure nothrow @nogc const
|
|
{
|
|
return data.uintLength;
|
|
}
|
|
|
|
/// Number of significant `ulong`s which are used in storing this number.
|
|
/// The absolute value of this `BigInt` is always < 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 = An OutputRange 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(Writer)(scope ref Writer sink, string formatString) const
|
|
{
|
|
auto f = FormatSpec!char(formatString);
|
|
f.writeUpToNextSpec(sink);
|
|
toString!Writer(sink, f);
|
|
}
|
|
|
|
/// ditto
|
|
void toString(Writer)(scope ref Writer sink, scope const ref FormatSpec!char f) const
|
|
{
|
|
import std.range.primitives : put;
|
|
const spec = f.spec;
|
|
immutable hex = (spec == 'x' || spec == 'X');
|
|
if (!(spec == 's' || spec == 'd' || spec =='o' || hex))
|
|
throw new FormatException("Format specifier not understood: %" ~ spec);
|
|
|
|
char[] buff;
|
|
if (spec == 'X')
|
|
{
|
|
buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.upper);
|
|
}
|
|
else if (spec == 'x')
|
|
{
|
|
buff = data.toHexString(0, '_', 0, f.flZero ? '0' : ' ', LetterCase.lower);
|
|
}
|
|
else if (spec == 'o')
|
|
{
|
|
buff = data.toOctalString();
|
|
}
|
|
else
|
|
{
|
|
buff = data.toDecimalString(0);
|
|
}
|
|
assert(buff.length > 0, "Invalid buffer length");
|
|
|
|
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)
|
|
put(sink, " ");
|
|
|
|
if (signChar)
|
|
{
|
|
scope char[1] buf = signChar;
|
|
put(sink, buf[]);
|
|
}
|
|
|
|
if (!f.flDash && f.flZero)
|
|
foreach (i; 0 .. difw)
|
|
put(sink, "0");
|
|
|
|
put(sink, buff);
|
|
|
|
if (f.flDash)
|
|
foreach (i; 0 .. difw)
|
|
put(sink, " ");
|
|
}
|
|
|
|
/**
|
|
`toString` is rarely directly invoked; the usual way of using it is via
|
|
$(REF format, std, format):
|
|
*/
|
|
@safe 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");
|
|
}
|
|
|
|
// for backwards compatibility, see unittest below
|
|
/// ditto
|
|
void toString(scope void delegate(scope const(char)[]) sink, string formatString) const
|
|
{
|
|
toString!(void delegate(scope const(char)[]))(sink, formatString);
|
|
}
|
|
|
|
// for backwards compatibility, see unittest below
|
|
/// ditto
|
|
void toString(scope void delegate(scope const(char)[]) sink, scope const ref FormatSpec!char f) const
|
|
{
|
|
toString!(void delegate(scope const(char)[]))(sink, f);
|
|
}
|
|
|
|
// Backwards compatibility test
|
|
// BigInt.toString used to only accept a delegate sink function, but this does not work
|
|
// well with attributes such as @safe. A template function toString was added that
|
|
// works on OutputRanges, but when a delegate was passed in the form of an untyped
|
|
// lambda such as `str => dst.put(str)` the parameter type was inferred as `void` and
|
|
// the function failed to instantiate.
|
|
@system unittest
|
|
{
|
|
import std.format.spec : FormatSpec;
|
|
import std.array : appender;
|
|
BigInt num = 503;
|
|
auto dst = appender!string();
|
|
num.toString(str => dst.put(str), null);
|
|
assert(dst[] == "503");
|
|
num = 504;
|
|
auto f = FormatSpec!char("");
|
|
num.toString(str => dst.put(str), f);
|
|
assert(dst[] == "503504");
|
|
}
|
|
|
|
// 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 pure nothrow @nogc
|
|
{
|
|
return data.toHash() + sign;
|
|
}
|
|
|
|
/**
|
|
`toHash` is rarely directly invoked; it is implicitly used when
|
|
BigInt is used as the key of an associative array.
|
|
*/
|
|
@safe pure unittest
|
|
{
|
|
string[BigInt] aa;
|
|
aa[BigInt(123)] = "abc";
|
|
aa[BigInt(456)] = "def";
|
|
|
|
assert(aa[BigInt(123)] == "abc");
|
|
assert(aa[BigInt(456)] == "def");
|
|
}
|
|
|
|
/**
|
|
* Gets the nth number in the underlying representation that makes up the whole
|
|
* `BigInt`.
|
|
*
|
|
* Params:
|
|
* T = the type to view the underlying representation as
|
|
* n = The nth number to retrieve. Must be less than $(LREF ulongLength) or
|
|
* $(LREF uintLength) with respect to `T`.
|
|
* Returns:
|
|
* The nth `ulong` in the representation of this `BigInt`.
|
|
*/
|
|
T getDigit(T = ulong)(size_t n) const
|
|
if (is(T == ulong) || is(T == uint))
|
|
{
|
|
static if (is(T == ulong))
|
|
{
|
|
assert(n < ulongLength(), "getDigit index out of bounds");
|
|
return data.peekUlong(n);
|
|
}
|
|
else
|
|
{
|
|
assert(n < uintLength(), "getDigit index out of bounds");
|
|
return data.peekUint(n);
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe pure unittest
|
|
{
|
|
auto a = BigInt("1000");
|
|
assert(a.ulongLength() == 1);
|
|
assert(a.getDigit(0) == 1000);
|
|
|
|
assert(a.uintLength() == 1);
|
|
assert(a.getDigit!uint(0) == 1000);
|
|
|
|
auto b = BigInt("2_000_000_000_000_000_000_000_000_000");
|
|
assert(b.ulongLength() == 2);
|
|
assert(b.getDigit(0) == 4584946418820579328);
|
|
assert(b.getDigit(1) == 108420217);
|
|
|
|
assert(b.uintLength() == 3);
|
|
assert(b.getDigit!uint(0) == 3489660928);
|
|
assert(b.getDigit!uint(1) == 1067516025);
|
|
assert(b.getDigit!uint(2) == 108420217);
|
|
}
|
|
|
|
private:
|
|
void negate() @safe pure nothrow @nogc scope
|
|
{
|
|
if (!data.isZero())
|
|
sign = !sign;
|
|
}
|
|
bool isZero() pure const nothrow @nogc @safe scope
|
|
{
|
|
return data.isZero();
|
|
}
|
|
alias isNegative = sign;
|
|
|
|
// Generate a runtime error if division by zero occurs
|
|
void checkDivByZero() pure const nothrow @safe scope
|
|
{
|
|
assert(!isZero(), "BigInt division by zero");
|
|
}
|
|
}
|
|
|
|
///
|
|
@safe 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";
|
|
BigInt k = j;
|
|
j ^^= 11;
|
|
assert(k ^^ 11 == j);
|
|
}
|
|
|
|
/**
|
|
Params:
|
|
x = The `BigInt` to convert to a decimal `string`.
|
|
|
|
Returns:
|
|
A `string` that represents the `BigInt` as a decimal number.
|
|
|
|
*/
|
|
string toDecimalString(const(BigInt) x) pure nothrow @safe
|
|
{
|
|
auto buff = x.data.toDecimalString(x.isNegative ? 1 : 0);
|
|
if (x.isNegative)
|
|
buff[0] = '-';
|
|
return buff;
|
|
}
|
|
|
|
///
|
|
@safe pure unittest
|
|
{
|
|
auto x = BigInt("123");
|
|
x *= 1000;
|
|
x += 456;
|
|
|
|
auto xstr = x.toDecimalString();
|
|
assert(xstr == "123456");
|
|
}
|
|
|
|
/**
|
|
Params:
|
|
x = The `BigInt` to convert to a hexadecimal `string`.
|
|
|
|
Returns:
|
|
A `string` that represents the `BigInt` as a hexadecimal (base 16)
|
|
number in upper case.
|
|
|
|
*/
|
|
string toHex(const(BigInt) x) pure @safe
|
|
{
|
|
import std.array : appender;
|
|
auto outbuff = appender!string();
|
|
x.toString(outbuff, "%X");
|
|
return outbuff[];
|
|
}
|
|
|
|
///
|
|
@safe 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 @safe
|
|
unittest
|
|
{
|
|
assert((-1).absUnsign == 1);
|
|
assert(1.absUnsign == 1);
|
|
}
|
|
|
|
nothrow pure @safe
|
|
unittest
|
|
{
|
|
BigInt a, b;
|
|
a = 1;
|
|
b = 2;
|
|
auto c = a + b;
|
|
assert(c == 3);
|
|
}
|
|
|
|
nothrow pure @safe
|
|
unittest
|
|
{
|
|
long a;
|
|
BigInt b;
|
|
auto c = a + b;
|
|
assert(c == 0);
|
|
auto d = b + a;
|
|
assert(d == 0);
|
|
}
|
|
|
|
nothrow pure @safe
|
|
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));
|
|
}
|
|
|
|
@safe 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; // https://issues.dlang.org/show_bug.cgi?id=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); // https://issues.dlang.org/show_bug.cgi?id=5928
|
|
assert(BigInt(-4) % BigInt(5) == -4);
|
|
assert(BigInt(2)/BigInt(-3) == BigInt(0)); // https://issues.dlang.org/show_bug.cgi?id=8022
|
|
assert(BigInt("-1") > long.min); // https://issues.dlang.org/show_bug.cgi?id=9548
|
|
|
|
assert(toDecimalString(BigInt("0000000000000000000000000000000000000000001234567"))
|
|
== "1234567");
|
|
}
|
|
|
|
@safe 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);
|
|
}
|
|
|
|
// Recursive division (https://issues.dlang.org/show_bug.cgi?id=5568)
|
|
@safe unittest
|
|
{
|
|
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);
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=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);
|
|
// https://issues.dlang.org/show_bug.cgi?id=7993
|
|
BigInt n7793 = 10;
|
|
assert( n7793 / 1 == 10);
|
|
// https://issues.dlang.org/show_bug.cgi?id=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);
|
|
// https://issues.dlang.org/show_bug.cgi?id=8165
|
|
BigInt[2] a8165;
|
|
a8165[0] = a8165[1] = 1;
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
import std.array;
|
|
import std.format.write : formattedWrite;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
import std.array;
|
|
import std.format.write : formattedWrite;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
import std.array;
|
|
import std.format.write : formattedWrite;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=6448
|
|
@safe unittest
|
|
{
|
|
import std.array;
|
|
import std.format.write : formattedWrite;
|
|
|
|
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);
|
|
// https://issues.dlang.org/show_bug.cgi?id=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.algebraic : abs;
|
|
auto r = abs(BigInt(-1000)); // https://issues.dlang.org/show_bug.cgi?id=6486
|
|
assert(r == 1000);
|
|
|
|
auto r2 = abs(const(BigInt)(-500)); // https://issues.dlang.org/show_bug.cgi?id=11188
|
|
assert(r2 == 500);
|
|
auto r3 = abs(immutable(BigInt)(-733)); // https://issues.dlang.org/show_bug.cgi?id=11188
|
|
assert(r3 == 733);
|
|
|
|
// opCast!bool
|
|
BigInt one = 1, zero;
|
|
assert(one && !zero);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=6850
|
|
@safe unittest
|
|
{
|
|
pure long pureTest() {
|
|
BigInt a = 1;
|
|
BigInt b = 1336;
|
|
a += b;
|
|
return a.toLong();
|
|
}
|
|
|
|
assert(pureTest() == 1337);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=8435
|
|
// https://issues.dlang.org/show_bug.cgi?id=10118
|
|
@safe unittest
|
|
{
|
|
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);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=11148
|
|
@safe unittest
|
|
{
|
|
void foo(BigInt) {}
|
|
const BigInt cbi = 3;
|
|
immutable BigInt ibi = 3;
|
|
|
|
foo(cbi);
|
|
foo(ibi);
|
|
|
|
import std.conv : to;
|
|
import std.meta : AliasSeq;
|
|
|
|
static foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt)))
|
|
{
|
|
static 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;
|
|
assert(n == 4);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=8167
|
|
@safe unittest
|
|
{
|
|
BigInt a = BigInt(3);
|
|
BigInt b = BigInt(a);
|
|
assert(b == 3);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=9061
|
|
@safe unittest
|
|
{
|
|
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);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=11600
|
|
@safe unittest
|
|
{
|
|
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"));
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=11583
|
|
@safe unittest
|
|
{
|
|
BigInt x = 0;
|
|
assert((x > 0) == false);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=13391
|
|
@safe unittest
|
|
{
|
|
BigInt x1 = "123456789";
|
|
BigInt x2 = "123456789123456789";
|
|
BigInt x3 = "123456789123456789123456789";
|
|
|
|
import std.meta : AliasSeq;
|
|
static 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);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=13963
|
|
@safe unittest
|
|
{
|
|
BigInt x = 1;
|
|
import std.meta : AliasSeq;
|
|
static foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int))
|
|
{
|
|
assert(is(typeof(x % Int(1)) == int));
|
|
}
|
|
assert(is(typeof(x % 1U) == long));
|
|
assert(is(typeof(x % 1L) == long));
|
|
assert(is(typeof(x % 1UL) == BigInt));
|
|
|
|
auto x0 = BigInt(uint.max - 1);
|
|
auto x1 = BigInt(8);
|
|
assert(x1 / x == x1);
|
|
auto x2 = -BigInt(long.min) + 1;
|
|
|
|
// uint
|
|
assert( x0 % uint.max == x0 % BigInt(uint.max));
|
|
assert(-x0 % uint.max == -x0 % BigInt(uint.max));
|
|
assert( x0 % uint.max == long(uint.max - 1));
|
|
assert(-x0 % uint.max == -long(uint.max - 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);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=14124
|
|
@safe unittest
|
|
{
|
|
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);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=15678
|
|
@safe unittest
|
|
{
|
|
import std.exception : assertThrown;
|
|
assertThrown!ConvException(BigInt(""));
|
|
assertThrown!ConvException(BigInt("0x1234BARF"));
|
|
assertThrown!ConvException(BigInt("1234PUKE"));
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=6447
|
|
@safe 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)
|
|
]));
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=17330
|
|
@safe unittest
|
|
{
|
|
auto b = immutable BigInt("123");
|
|
assert(b == 123);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=14767
|
|
@safe pure unittest
|
|
{
|
|
static immutable a = BigInt("340282366920938463463374607431768211455");
|
|
assert(a == BigInt("340282366920938463463374607431768211455"));
|
|
|
|
BigInt plusTwo(in BigInt n)
|
|
{
|
|
return n + 2;
|
|
}
|
|
|
|
enum BigInt test1 = BigInt(123);
|
|
enum BigInt test2 = plusTwo(test1);
|
|
assert(test2 == 125);
|
|
}
|
|
|
|
/**
|
|
* Finds the quotient and remainder for the given dividend and divisor in one operation.
|
|
*
|
|
* Params:
|
|
* dividend = the $(LREF BigInt) to divide
|
|
* divisor = the $(LREF BigInt) to divide the dividend by
|
|
* quotient = is set to the result of the division
|
|
* remainder = is set to the remainder of the division
|
|
*/
|
|
void divMod(const BigInt dividend, const BigInt divisor, out BigInt quotient, out BigInt remainder) pure nothrow @safe
|
|
{
|
|
BigUint q, r;
|
|
BigUint.divMod(dividend.data, divisor.data, q, r);
|
|
quotient.sign = dividend.sign != divisor.sign;
|
|
quotient.data = q;
|
|
remainder.sign = r.isZero() ? false : dividend.sign;
|
|
remainder.data = r;
|
|
}
|
|
|
|
///
|
|
@safe pure nothrow unittest
|
|
{
|
|
auto a = BigInt(123);
|
|
auto b = BigInt(25);
|
|
BigInt q, r;
|
|
|
|
divMod(a, b, q, r);
|
|
|
|
assert(q == 4);
|
|
assert(r == 23);
|
|
assert(q * b + r == a);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=18086
|
|
@safe pure nothrow unittest
|
|
{
|
|
BigInt q = 1;
|
|
BigInt r = 1;
|
|
BigInt c = 1024;
|
|
BigInt d = 100;
|
|
|
|
divMod(c, d, q, r);
|
|
assert(q == 10);
|
|
assert(r == 24);
|
|
assert((q * d + r) == c);
|
|
|
|
divMod(c, -d, q, r);
|
|
assert(q == -10);
|
|
assert(r == 24);
|
|
assert(q * -d + r == c);
|
|
|
|
divMod(-c, -d, q, r);
|
|
assert(q == 10);
|
|
assert(r == -24);
|
|
assert(q * -d + r == -c);
|
|
|
|
divMod(-c, d, q, r);
|
|
assert(q == -10);
|
|
assert(r == -24);
|
|
assert(q * d + r == -c);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=22771
|
|
@safe pure nothrow unittest
|
|
{
|
|
BigInt quotient, remainder;
|
|
divMod(BigInt(-50), BigInt(1), quotient, remainder);
|
|
assert(remainder == 0);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=19740
|
|
@safe unittest
|
|
{
|
|
BigInt a = BigInt(
|
|
"241127122100380210001001124020210001001100000200003101000062221012075223052000021042250111300200000000000" ~
|
|
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
|
BigInt b = BigInt(
|
|
"700200000000500418321000401140010110000022007221432000000141020011323301104104060202100200457210001600142" ~
|
|
"000001012245300100001110215200000000120000000000000000000000000000000000000000000000000000000000000000000" ~
|
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
|
|
|
BigInt c = a * b;
|
|
assert(c == BigInt(
|
|
"1688372108948068874722901180228375682334987075822938736581472847151834613694489486296103575639363261807341" ~
|
|
"3910091006778604956808730652275328822700182498926542563654351871390166691461743896850906716336187966456064" ~
|
|
"2702007176328110013356024000000000000000000000000000000000000000000000000000000000000000000000000000000000" ~
|
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ~
|
|
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
auto n = BigInt("1234"d);
|
|
}
|
|
|
|
/**
|
|
Fast power modulus calculation for $(LREF BigInt) operands.
|
|
Params:
|
|
base = the $(LREF BigInt) is basic operands.
|
|
exponent = the $(LREF BigInt) is power exponent of base.
|
|
modulus = the $(LREF BigInt) is modules to be modular of base ^ exponent.
|
|
Returns:
|
|
The power modulus value of (base ^ exponent) % modulus.
|
|
*/
|
|
BigInt powmod(BigInt base, BigInt exponent, BigInt modulus) pure nothrow @safe
|
|
{
|
|
BigInt result = 1;
|
|
|
|
while (exponent)
|
|
{
|
|
if (exponent.data.peekUint(0) & 1)
|
|
{
|
|
result = (result * base) % modulus;
|
|
}
|
|
|
|
auto tmp = base % modulus;
|
|
base = (tmp * tmp) % modulus;
|
|
exponent >>= 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// for powmod
|
|
@safe unittest
|
|
{
|
|
BigInt base = BigInt("123456789012345678901234567890");
|
|
BigInt exponent = BigInt("1234567890123456789012345678901234567");
|
|
BigInt modulus = BigInt("1234567");
|
|
|
|
BigInt result = powmod(base, exponent, modulus);
|
|
assert(result == 359079);
|
|
}
|