1577 lines
37 KiB
D

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Arbitrary precision arithmetic.
*
* Copyright: Eugene Wissner 2016-2018.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/math/mp.d,
* tanya/math/mp.d)
*/
module tanya.math.mp;
import std.algorithm.comparison;
import std.algorithm.mutation : copy, fill, reverse;
import std.range;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.encoding.ascii;
import tanya.memory;
static import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Algebraic sign.
*/
enum Sign : bool
{
/// The value is positive or `0`.
positive = false,
/// The value is negative.
negative = true,
}
/**
* Mutliple precision integer.
*/
struct Integer
{
package digit[] rep;
package ptrdiff_t size;
package Sign sign;
pure nothrow @safe @nogc invariant
{
assert(this.size > 0 || !this.sign, "0 should be positive.");
}
private alias digit = uint;
private alias word = ulong;
// Count of bits per digit.
private enum : digit
{
digitBitCount = 28,
mask = 0xfffffff,
}
/**
* Creates a multiple precision integer.
*
* Params:
* T = Value type.
* value = Initial value.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this(T)(const T value, shared Allocator allocator = defaultAllocator)
if (isIntegral!T)
{
this(allocator);
this = value;
}
/// ditto
this(T)(ref T value, shared Allocator allocator = defaultAllocator)
if (is(Unqual!T == Integer))
{
this(allocator);
this = value;
}
/// ditto
this(T)(T value, shared Allocator allocator = defaultAllocator)
if (is(T == Integer))
{
this(allocator);
if (allocator is value.allocator)
{
this.rep = value.rep;
this.size = value.size;
this.sign = value.sign;
value.rep = null;
value.size = 0;
value.sign = Sign.positive;
}
else
{
this = value;
}
}
/// ditto
this(shared Allocator allocator) @nogc nothrow pure @safe
in
{
assert(allocator !is null);
}
do
{
this.allocator_ = allocator;
}
/**
* Constructs the integer from a sign-magnitude $(D_KEYWORD ubyte) range.
*
* Params:
* R = Range type.
* sign = Sign.
* value = Range.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this(R)(const Sign sign,
R value,
shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
{
this(allocator);
grow(value.length / (digitBitCount / 8) + 1);
int bit, delta;
for (; !value.empty; ++this.size)
{
word w;
for (bit = delta; (bit < digitBitCount) && !value.empty; bit += 8)
{
w |= (cast(word) value.back) << bit;
value.popBack();
}
delta = bit - digitBitCount;
this.rep[this.size] |= w & mask;
if (delta > 0)
{
this.rep[this.size + 1] = (w >> digitBitCount) & mask;
}
}
}
///
@nogc nothrow pure @safe unittest
{
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(Sign.positive, range[]);
assert(integer == 7383520307673030126);
}
/**
* Constructs the integer from a two's complement representation.
*
* Params:
* R = Range type.
* value = Range.
* allocator = Allocator.
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
this(R)(R value, shared Allocator allocator = defaultAllocator)
if (isBidirectionalRange!R && hasLength!R
&& is(Unqual!(ElementType!R) == ubyte))
{
this(Sign.positive, value, allocator);
if (!value.empty && ((value.front & 0x80) != 0))
{
// Negative number.
opOpAssign!"-"(exp2(countBits()));
}
}
///
@nogc nothrow pure @safe unittest
{
{
ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(range[]);
assert(integer == 7383520307673030126);
}
{
ubyte[8] range = [ 0xe6, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(range[]);
assert(integer == -1839851729181745682);
}
}
/**
* Copies the integer.
*/
this(this) @nogc nothrow pure @safe
{
auto tmp = allocator.resize!digit(null, this.size);
tanya.memory.op.copy(this.rep[0 .. this.size], tmp);
this.rep = tmp;
}
/**
* Destroys the integer.
*/
~this() @nogc nothrow pure @safe
{
allocator.resize(this.rep, 0);
}
static private const short[16] bitCounts = [
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
];
// Counts the number of LSBs before the first non-zero bit.
private ptrdiff_t countLSBs() const @nogc nothrow pure @safe
{
if (this.size == 0)
{
return 0;
}
ptrdiff_t bits;
for (bits = 0; (bits < this.size) && (this.rep[bits] == 0); ++bits)
{
}
digit nonZero = this.rep[bits];
bits *= digitBitCount;
/* now scan this digit until a 1 is found */
if ((nonZero & 0x01) == 0)
{
digit bitCountsPos;
do
{
bitCountsPos = nonZero & 0x0f;
bits += bitCounts[bitCountsPos];
nonZero >>= 4;
}
while (bitCountsPos == 0);
}
return bits;
}
/**
* Returns: Number of bytes in the two's complement representation.
*/
@property size_t length() const @nogc nothrow pure @safe
{
if (this.sign)
{
const bc = countBits();
auto length = bc + (8 - (bc & 0x07));
if (((countLSBs() + 1) == bc) && ((bc & 0x07) == 0))
{
--length;
}
return length / 8;
}
else if (this.size == 0)
{
return 0;
}
else
{
return (countBits() / 8) + 1;
}
}
///
@nogc nothrow pure @safe unittest
{
{
Integer i;
assert(i.length == 0);
}
{
auto i = Integer(-123456789);
assert(i.length == 4);
}
}
/**
* Assigns a new value.
*
* Params:
* T = Value type.
* value = Value.
*
* Returns: $(D_KEYWORD this).
*/
ref Integer opAssign(T)(const T value)
if (isIntegral!T)
{
rep[0 .. this.size].fill(digit.init);
grow(digitBitCount / 8 + 1);
static if (isSigned!T)
{
ulong absolute;
if (value >= 0)
{
absolute = value;
this.sign = Sign.positive;
}
else
{
absolute = -value;
this.sign = Sign.negative;
}
}
else
{
ulong absolute = value;
this.sign = Sign.positive;
}
for (this.size = 0; absolute; absolute >>= digitBitCount, ++this.size)
{
this.rep[this.size] = absolute & mask;
}
return this;
}
/// ditto
ref Integer opAssign(T)(ref T value)
if (is(Unqual!T == Integer))
{
this.rep = allocator.resize(this.rep, value.size);
tanya.memory.op.copy(value.rep[0 .. value.size],
this.rep[0 .. value.size]);
this.size = value.size;
this.sign = value.sign;
return this;
}
/// ditto
ref Integer opAssign(T)(T value)
if (is(T == Integer))
{
swap(this.rep, value.rep);
swap(this.sign, value.sign);
swap(this.size, value.size);
swap(this.allocator_, value.allocator_);
return this;
}
/**
* Casting.
*
* Params:
* T = Target type.
*
* Returns: Converted value.
*/
T opCast(T : bool)() const
{
return this.size > 0;
}
/// ditto
T opCast(T)() const
if (isIntegral!T && isUnsigned!T)
{
T ret;
ubyte shift;
for (size_t i; i < this.size && shift <= T.sizeof * 8; ++i)
{
ret |= (cast(T) this.rep[i]) << shift;
shift += digitBitCount;
}
return ret;
}
/// ditto
T opCast(T)() const
if (isIntegral!T && isSigned!T)
{
return this.sign
? cast(T) -(cast(Promoted!(Unsigned!T)) (cast(Unsigned!T) this))
: cast(Unsigned!T) this;
}
///
@nogc nothrow pure @safe unittest
{
auto integer = Integer(79);
assert(cast(ushort) integer == 79);
integer = -79;
assert(cast(short) integer == -79);
integer = 4294967295;
assert(cast(long) integer == 4294967295);
integer = -4294967295;
assert(cast(long) integer == -4294967295);
integer = long.min;
assert(cast(long) integer == long.min);
integer = long.min + 1;
assert(cast(long) integer == long.min + 1);
integer = 0;
assert(cast(long) integer == 0);
}
/* Trim unused digits.
*
* This is used to ensure that leading zero digits are
* trimed and the leading "size" digit will be non-zero
* Typically very fast. Also fixes the sign if there
* are no more leading digits
*/
void contract() @nogc nothrow pure @safe
{
/* decrease size while the most significant digit is
* zero.
*/
while ((this.size > 0) && (this.rep[this.size - 1] == 0))
{
--this.size;
}
/* reset the sign flag if size == 0 */
if (this.size == 0)
{
this.sign = Sign.positive;
}
}
private void grow(const size_t size) @nogc nothrow pure @safe
{
if (this.rep.length >= size)
{
return;
}
const oldLength = this.rep.length;
allocator.resize(this.rep, size);
this.rep[oldLength .. $].fill(digit.init);
}
private size_t countBits() const @nogc nothrow pure @safe
{
if (this.size == 0)
{
return 0;
}
auto r = (this.size - 1) * digitBitCount;
digit q = this.rep[this.size - 1];
while (q > 0)
{
++r;
q >>= (cast(digit) 1);
}
return r;
}
private void add(ref const Integer summand, ref Integer sum)
const @nogc nothrow pure @safe
{
const(digit)[] max, min;
if (this.size > summand.size)
{
min = summand.rep[0 .. summand.size];
max = this.rep[0 .. this.size];
}
else
{
min = this.rep[0 .. this.size];
max = summand.rep[0 .. summand.size];
}
sum.grow(max.length + 1);
const oldSize = sum.size;
sum.size = cast(int) (max.length + 1);
auto result = sum.rep;
digit carry;
foreach (i, ref const d; min)
{
result.front = d + max.front + carry;
carry = result.front >> digitBitCount;
result.front &= mask;
max.popFront();
result.popFront();
}
// Copy higher digests if one of the summands is greater than another
// one.
for (; !max.empty; max.popFront(), result.popFront())
{
result.front = max.front + carry;
carry = result.front >> digitBitCount;
result.front &= mask;
}
result.front = carry;
// Clear digits above the old size.
for (auto i = sum.size; i < oldSize; ++i)
{
sum.rep[i] = 0;
}
sum.contract();
}
private void add(const digit summand, ref Integer sum)
const @nogc nothrow pure @safe
{
sum.grow(this.size + 2);
sum.rep[0] = this.rep[0] + summand;
auto carry = sum.rep[0] >> digitBitCount;
sum.rep[0] &= mask;
size_t i;
for (i = 1; i < this.size; ++i)
{
sum.rep[i] = this.rep[i] + carry;
carry = sum.rep[i] >> digitBitCount;
sum.rep[i] &= mask;
}
sum.rep[i++] = carry;
for (; i < sum.size; ++i)
{
sum.rep[i] = 0;
}
sum.size = this.size + 1;
sum.contract();
}
private void subtract(ref const Integer subtrahend, ref Integer difference)
const @nogc nothrow pure @safe
{
difference.grow(this.size);
const oldSize = difference.size;
difference.size = this.size;
size_t i;
digit carry;
for (i = 0; i < subtrahend.size; ++i)
{
difference.rep[i] = (this.rep[i] - subtrahend.rep[i]) - carry;
carry = difference.rep[i] >> (cast(digit) (8 * digit.sizeof) - 1);
difference.rep[i] &= mask;
}
// Copy higher digests if the minuend has more digits than the
// subtrahend.
for (; i < this.size; ++i)
{
difference.rep[i] = this.rep[i] - carry;
carry = difference.rep[i] >> (cast(digit) ((8 * digit.sizeof) - 1));
difference.rep[i] &= mask;
}
// Clear digits above the size.
for (i = difference.size; i < oldSize; ++i)
{
difference.rep[i] = 0;
}
difference.contract();
}
private void subtract(const digit subtrahend, ref Integer difference)
const @nogc nothrow pure @safe
{
difference.grow(this.size);
const oldSize = difference.size;
difference.sign = this.sign;
difference.size = this.size;
difference.rep[0] = this.rep[0] - subtrahend;
auto carry = difference.rep[0] >> ((digit.sizeof * 8) - 1);
difference.rep[0] &= mask;
size_t i;
for (i = 1; i < this.size; ++i)
{
difference.rep[i] = this.rep[i] - carry;
carry = difference.rep[i] >> ((digit.sizeof * 8) - 1);
difference.rep[i] &= mask;
}
for (; i < oldSize; ++i)
{
difference.rep[i] = 0;
}
difference.contract();
}
// Compare the magnitude.
private int compare(ref const Integer that) const @nogc nothrow pure @safe
{
if (this.size > that.size)
{
return 1;
}
else if (this.size < that.size)
{
return -1;
}
return this.rep[0 .. this.size]
.retro
.cmp(that.rep[0 .. that.size].retro);
}
/**
* Comparison.
*
* Params:
* I = Comparand type.
* that = The second integer.
*
* Returns: A positive number if $(D_INLINECODE this > that), a negative
* number if $(D_INLINECODE this < that), `0` otherwise.
*/
int opCmp(I : Integer)(auto ref const I that) const
{
if (this.sign != that.sign)
{
if (this.sign == Sign.negative)
{
return -1;
}
else
{
return 1;
}
}
if (this.sign == Sign.negative)
{
return that.compare(this);
}
else
{
return compare(that);
}
}
///
@nogc nothrow pure @safe unittest
{
auto integer1 = Integer(1019);
auto integer2 = Integer(1019);
assert(integer1 == integer2);
integer2 = 3337;
assert(integer1 < integer2);
integer2 = 688;
assert(integer1 > integer2);
integer2 = -3337;
assert(integer1 > integer2);
}
/// ditto
int opCmp(I)(const I that) const
if (isIntegral!I)
{
if (that < 0 && !this.sign)
{
return 1;
}
else if (that > 0 && this.sign)
{
return -1;
}
else if (this.size > I.sizeof)
{
return this.sign ? -1 : 1;
}
const diff = (cast(I) this) - that;
if (diff > 0)
{
return 1;
}
else if (diff < 0)
{
return -1;
}
return 0;
}
///
@nogc nothrow pure @safe unittest
{
auto integer = Integer(1019);
assert(integer == 1019);
assert(integer < 3337);
assert(integer > 688);
assert(integer > -3337);
}
/**
* Params:
* I = Comparand type.
* that = The second integer.
*
* Returns: Whether the two integers are equal.
*/
bool opEquals(I)(auto ref const I that) const
if (is(I : Integer) || isIntegral!I)
{
return opCmp!I(that) == 0;
}
///
@nogc nothrow pure @safe unittest
{
auto integer = Integer(1019);
assert(integer == Integer(1019));
assert(integer != Integer(109));
}
/**
* Assignment operators with another $(D_PSYMBOL Integer).
*
* Params:
* op = Operation.
* operand = The second operand.
*
* Returns: $(D_KEYWORD this).
*/
ref Integer opOpAssign(string op : "+")(auto ref const Integer operand)
{
if (this.sign == operand.sign)
{
add(operand, this);
}
else if (compare(operand) < 0)
{
this.sign = operand.sign;
operand.subtract(this, this);
}
else
{
subtract(operand, this);
}
return this;
}
///
@nogc nothrow pure @safe unittest
{
{
auto h1 = Integer(1019);
auto h2 = Integer(3337);
h1 += h2;
assert(h1 == 4356);
}
{
auto h1 = Integer(4356);
auto h2 = Integer(2_147_483_647);
h1 += h2;
assert(h1 == 2147488003);
}
{
auto h1 = Integer(2147488003L);
auto h2 = Integer(2_147_483_647);
h1 += h2;
assert(h1 == 4294971650);
}
}
/// ditto
ref Integer opOpAssign(string op : "-")(auto ref const Integer operand)
{
if (this.sign != operand.sign)
{
add(operand, this);
}
else if (compare(operand) >= 0)
{
subtract(operand, this);
}
else
{
operand.subtract(this, this);
this.sign = this.sign ? Sign.positive : Sign.negative;
}
return this;
}
///
@nogc nothrow pure @safe unittest
{
{
auto h1 = Integer(3);
auto h2 = Integer(4);
h1 -= h2;
assert(h1 == -1);
}
{
auto h1 = Integer(8589934590L);
auto h2 = Integer(2147483647);
h1 -= h2;
assert(h1 == 6442450943);
}
{
auto h1 = Integer(6442450943);
auto h2 = Integer(4294967294);
h1 -= h2;
assert(h1 == 2147483649);
}
{
auto h1 = Integer(2147483649);
auto h2 = Integer(h1);
h1 -= h2;
assert(h1 == 0);
}
}
/// ditto
ref Integer opOpAssign(string op : "*")(auto ref const Integer operand)
{
const digits = this.size + operand.size + 1;
multiply(operand, this, digits);
if (this.size > 0)
{
this.sign = this.sign == operand.sign ? Sign.positive : Sign.negative;
}
return this;
}
///
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(123);
auto h2 = Integer(456);
h1 *= h2;
assert(h1 == 56088);
}
/// ditto
ref Integer opOpAssign(string op : "/")(auto ref const Integer operand)
in
{
assert(operand.length > 0, "Division by zero.");
}
do
{
divide(operand, this);
return this;
}
/// ditto
ref Integer opOpAssign(string op : "%")(auto ref const Integer operand)
in
{
assert(operand.length > 0, "Division by zero.");
}
do
{
divide(operand, null, this);
return this;
}
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(18);
auto h2 = Integer(4);
h1 %= h2;
assert(h1 == 2);
h1 = 8;
h1 %= h2;
assert(h1 == 0);
h1 = 7;
h1 %= h2;
assert(h1 == 3);
h1 = 56088;
h2 = 456;
h1 /= h2;
assert(h1 == 123);
}
/// ditto
ref Integer opOpAssign(string op : ">>")(const size_t operand)
{
if (operand == 0)
{
return this;
}
if (operand >= digitBitCount)
{
shiftRight(operand / digitBitCount);
}
const bit = cast(digit) (operand % digitBitCount);
if (bit != 0)
{
const mask = ((cast(digit) 1) << bit) - 1;
const shift = digitBitCount - bit;
digit carry;
foreach (ref d; this.rep[0 .. this.size].retro)
{
const newCarry = d & mask;
d = (d >> bit) | (carry << shift);
carry = newCarry;
}
}
this.contract();
return this;
}
///
@nogc nothrow pure @safe unittest
{
auto integer = Integer(4294967294);
integer >>= 10;
assert(integer == 4194303);
integer = 27336704;
integer >>= 1;
assert(integer == 13668352);
integer = 4294967294;
integer >>= 20;
assert(integer == 4095);
integer >>= 0;
assert(integer == 4095);
integer >>= 20;
assert(integer == 0);
integer >>= 2;
assert(integer == 0);
integer = 1431655765;
integer >>= 16;
assert(integer == 21845);
integer >>= 16;
assert(integer == 0);
}
/// ditto
ref Integer opOpAssign(string op : "<<")(const size_t operand)
{
const step = operand / digitBitCount;
if (this.rep.length < this.size + step + 1)
{
grow(this.size + step + 1);
}
if (operand >= digitBitCount)
{
shiftLeft(step);
}
const bit = cast(digit) (operand % digitBitCount);
if (bit != 0)
{
const mask = ((cast(digit) 1) << bit) - 1;
const shift = digitBitCount - bit;
digit carry;
foreach (ref d; this.rep[0 .. this.size])
{
const newCarry = (d >> shift) & mask;
d = ((d << bit) | carry) & this.mask;
carry = newCarry;
}
if (carry != 0)
{
this.rep[this.size++] = carry;
}
}
this.contract();
return this;
}
///
@nogc nothrow pure @safe unittest
{
auto integer = Integer(4294967295);
integer <<= 1;
assert(integer == 8589934590);
}
/**
* Unary operators.
*
* Params:
* op = Operation.
*
* Returns: New $(D_PSYMBOL Integer).
*/
Integer opUnary(string op : "~")() const
{
auto ret = Integer(this, allocator);
foreach (ref a; ret.rep[0 .. ret.size])
{
a = ~a & mask;
}
return ret;
}
/// ditto
Integer opUnary(string op : "-")() const
{
auto ret = Integer(this, allocator);
ret.sign = ret.sign ? Sign.positive : Sign.negative;
return ret;
}
/**
* Unary operators.
*
* Params:
* op = Operation.
*
* Returns: $(D_KEYWORD this).
*/
ref inout(Integer) opUnary(string op : "+")() inout
{
return this;
}
//
@nogc nothrow pure @safe unittest
{
auto h1 = Integer(79);
Integer h2;
h2 = +h1;
assert(h2 == 79);
h2 = -h1;
assert(h2 == -79);
assert(h1 == 79);
h1 = -h2;
assert(h1 == 79);
h2 = ~h1;
assert(h2 == cast(ubyte) ~79);
}
/// ditto
ref Integer opUnary(string op : "++")()
{
if (this.sign)
{
subtract(1, this);
}
else
{
add(1, this);
}
return this;
}
/// ditto
ref Integer opUnary(string op : "--")()
{
if (this.size == 0)
{
add(1, this);
this.sign = Sign.negative;
}
else if (this.sign)
{
add(1, this);
}
else
{
subtract(1, this);
}
return this;
}
///
@nogc nothrow pure @safe unittest
{
Integer integer;
++integer;
assert(integer == 1);
--integer;
assert(integer == 0);
integer = 511;
++integer;
assert(integer == 512);
--integer;
assert(integer == 511);
integer = 79;
++integer;
assert(integer == 80);
--integer;
assert(integer == 79);
integer = -2;
++integer;
assert(integer == -1);
++integer;
assert(integer == 0);
--integer;
assert(integer == -1);
}
/**
* Implements binary operators.
*
* Params:
* op = Operation.
* operand = The second operand.
*
* Returns: Result.
*/
Integer opBinary(string op)(auto ref const Integer operand) const
if ((op == "+" || op == "-") || (op == "*"))
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// ditto
Integer opBinary(string op)(const auto ref Integer operand) const
if (op == "/" || op == "%")
in
{
assert(operand.length > 0, "Division by zero.");
}
do
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// ditto
Integer opBinary(string op)(const size_t operand) const
if (op == "<<" || op == ">>")
{
mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
// Shift right a certain amount of digits.
private void shiftRight(const size_t operand) @nogc nothrow pure @safe
{
if (operand == 0)
{
return;
}
if (this.size <= operand)
{
this = 0;
return;
}
const reducedSize = this.size - operand;
this.rep[operand .. this.size].copy(this.rep[0 .. reducedSize]);
this.rep[reducedSize .. this.size].fill(digit.init);
this.size = reducedSize;
}
// Shift left a certain amount of digits.
private void shiftLeft(const size_t operand) @nogc nothrow pure @safe
{
if (operand == 0)
{
return;
}
const increasedSize = this.size + operand;
grow(increasedSize);
this.size = increasedSize;
auto top = this.size - 1;
auto bottom = this.size - 1 - operand;
for (; top >= operand; --bottom, --top)
{
this.rep[top] = this.rep[bottom];
}
this.rep[0 .. operand].fill(digit.init);
}
private void multiply(const digit factor, ref Integer product)
const @nogc nothrow pure @safe
{
product.grow(this.size + 1);
product.sign = this.sign;
word carry;
size_t i;
for (i = 0; i < this.size; ++i)
{
auto newCarry = carry + (cast(word) this.rep[i] * factor);
product.rep[i] = newCarry & mask;
carry = newCarry >> digitBitCount;
}
product.rep[i++] = carry & mask;
for (; i < this.size; ++i)
{
product.rep[i] = 0;
}
product.size = this.size + 1;
product.contract();
}
private void multiply(ref const Integer factor,
ref Integer product,
const size_t digits) const @nogc nothrow pure @safe
{
Integer intermediate;
intermediate.grow(digits);
intermediate.size = digits;
for (size_t i; i < this.size; ++i)
{
const limit = min(factor.size, digits - i);
word carry;
auto k = i;
for (size_t j; j < limit; ++j, ++k)
{
const result = cast(word) intermediate.rep[k]
+ (cast(word) this.rep[i] * factor.rep[j])
+ carry;
intermediate.rep[k] = result & mask;
carry = result >> digitBitCount;
}
if (k < digits)
{
intermediate.rep[k] = carry & mask;
}
}
intermediate.contract();
swap(product, intermediate);
}
private void divide(Q, ARGS...)(ref const Integer divisor,
auto ref Q quotient,
ref ARGS args) const
if ((is(Q : typeof(null))
|| (is(Q : Integer) && __traits(isRef, quotient)))
&& (ARGS.length == 0 || (ARGS.length == 1 && is(ARGS[0] : Integer))))
in
{
assert(divisor != 0, "Division by zero.");
}
do
{
if (compare(divisor) < 0)
{
static if (ARGS.length == 1)
{
args[0] = this;
}
static if (!is(Q == typeof(null)))
{
quotient = 0;
}
return;
}
Integer q, t1, t2;
q.grow(this.size + 2);
q.size = this.size + 2;
t1.grow(2);
t2.grow(3);
auto x = Integer(this);
auto y = Integer(divisor);
const sign = this.sign == divisor.sign ? Sign.positive : Sign.negative;
x.sign = y.sign = Sign.positive;
auto norm = y.countBits() % digitBitCount;
if (norm < digitBitCount - 1)
{
norm = digitBitCount - 1 - norm;
x <<= norm;
y <<= norm;
}
else
{
norm = 0;
}
auto n = x.size - 1;
auto t = y.size - 1;
y.shiftLeft(n - t);
while (x >= y)
{
++q.rep[n - t];
x -= y;
}
y.shiftRight(n - t);
for (auto i = n; i >= (t + 1); --i)
{
if (i > x.size)
{
continue;
}
if (x.rep[i] == y.rep[t])
{
q.rep[(i - t) - 1] = (((cast(digit) 1) << digitBitCount) - 1);
}
else
{
word tmp = (cast(word) x.rep[i]) << digitBitCount;
tmp |= x.rep[i - 1];
tmp /= y.rep[t];
if (tmp > mask)
{
tmp = mask;
}
q.rep[i - t - 1] = tmp & mask;
}
q.rep[i - t - 1] = (q.rep[i - t - 1] + 1) & mask;
do
{
q.rep[i - t - 1] = (q.rep[i - t - 1] - 1) & mask;
// Left hand.
t1 = 0;
t1.rep[0] = ((t - 1) < 0) ? 0 : y.rep[t - 1];
t1.rep[1] = y.rep[t];
t1.size = 2;
t1.multiply(q.rep[i - t - 1], t1);
// Right hand.
t2.rep[0] = ((i - 2) < 0) ? 0 : x.rep[i - 2];
t2.rep[1] = ((i - 1) < 0) ? 0 : x.rep[i - 1];
t2.rep[2] = x.rep[i];
t2.size = 3;
}
while (t1.compare(t2) > 0);
y.multiply(q.rep[i - t - 1], t1);
t1.shiftLeft(i - t - 1);
x -= t1;
if (x.sign == Sign.negative)
{
t1 = y;
t1.shiftLeft(i - t - 1);
x += t1;
q.rep[i - t - 1] = (q.rep[i - t - 1] - 1) & mask;
}
}
x.sign = (x.size == 0) ? Sign.positive : this.sign;
static if (!is(Q == typeof(null)))
{
q.contract();
swap(q, quotient);
quotient.sign = sign;
}
static if (ARGS.length == 1)
{
x >>= norm;
swap(x, args[0]);
}
}
private Integer square() @nogc nothrow pure @safe
{
Integer result;
const resultSize = 2 * this.size + 1;
result.grow(resultSize);
result.size = resultSize;
for (size_t i; i < this.size; ++i)
{
const doubleI = 2 * i;
word product = cast(word) result.rep[doubleI]
+ (cast(word) this.rep[i] * this.rep[i]);
result.rep[doubleI] = product & mask;
word carry = product >> digitBitCount;
size_t k = doubleI + 1;
for (auto j = i + 1; j < this.size; ++j, ++k)
{
product = (cast(word) this.rep[i]) * (cast(word) this.rep[j]);
product = (cast(word) result.rep[k]) + (2 * product) + carry;
result.rep[k] = product & mask;
carry = product >> digitBitCount;
}
for (; carry != 0; ++k)
{
product = (cast(word) result.rep[k]) + carry;
result.rep[k] = product & mask;
carry = product >> digitBitCount;
}
}
result.contract();
return result;
}
// Returns 2^^n.
private Integer exp2(size_t n) const @nogc nothrow pure @safe
{
auto ret = Integer(allocator);
const bytes = n / digitBitCount;
ret.grow(bytes + 1);
ret.size = bytes + 1;
ret.rep[bytes] = (cast(digit) 1) << (n % digitBitCount);
return ret;
}
/**
* Returns: Two's complement representation of the integer.
*/
Array!ubyte toArray() const @nogc nothrow pure @safe
out (array)
{
assert(array.length == length);
}
do
{
Array!ubyte array;
if (this.size == 0)
{
return array;
}
const bc = countBits();
const remainingBits = bc & 0x07;
array.reserve(bc / 8);
if (remainingBits == 0)
{
array.insertBack(ubyte.init);
}
Integer tmp;
if (this.sign)
{
auto length = bc + (8 - remainingBits);
if (((countLSBs() + 1) == bc) && (remainingBits == 0))
{
length -= 8;
}
tmp = exp2(length) + this;
}
else
{
tmp = this;
}
do
{
array.insertBack(cast(ubyte) (tmp.rep[0] & 0xff));
tmp >>= 8;
}
while (tmp != 0);
array[].reverse();
return array;
}
///
@nogc nothrow pure @safe unittest
{
{
auto integer = Integer(0x66778899aabbddee);
ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
}
@nogc nothrow pure @safe unittest
{
{
Integer integer;
assert(integer.toArray().length == 0);
}
{
auto integer = Integer(0x03);
ubyte[1] expected = [ 0x03 ];
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
{
ubyte[63] expected = [
0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x3b, 0x00, 0x61, 0x62, 0x63,
];
auto integer = Integer(Sign.positive, expected[]);
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
{
ubyte[14] expected = [
0x22, 0x33, 0x44, 0x55, 0x05, 0x06, 0x07,
0x08, 0x3a, 0x3b, 0x00, 0x61, 0x62, 0x63,
];
auto integer = Integer(Sign.positive, expected[]);
auto array = integer.toArray();
assert(equal(array[], expected[]));
}
}
mixin DefaultAllocator;
}