Use RefCounted as math.mp.Integer internal storage

This commit is contained in:
Eugen Wissner 2016-12-04 22:51:21 +01:00
parent 1c5796eb96
commit 86c08e7af6
1 changed files with 142 additions and 67 deletions

View File

@ -11,17 +11,36 @@
module tanya.math.mp; module tanya.math.mp;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation;
import std.algorithm.searching; import std.algorithm.searching;
import std.algorithm.mutation;
import std.experimental.allocator;
import tanya.memory.allocator;
import tanya.memory.types;
struct Integer struct Integer
{ {
private ubyte[] rep; private RefCounted!(ubyte[]) rep;
private bool sign; private bool sign;
this(in uint value) /**
* Creates a multiple precision integer.
*
* Params:
* value = Initial value.
* allocator = Allocator.
*/
this(in uint value, IAllocator allocator = theAllocator)
in
{ {
opAssign(value); assert(allocator !is null);
}
body
{
this(allocator);
immutable size = calculateSizeFromInt(value);
rep = allocator.makeArray!ubyte(size);
assignInt(size, value);
} }
/// ///
@ -32,24 +51,37 @@ struct Integer
assert(h.rep[0] == 79); assert(h.rep[0] == 79);
} }
this(in Integer value) /// Ditto.
this(in Integer value, IAllocator allocator = theAllocator)
in
{ {
opAssign(value); assert(allocator !is null);
}
body
{
this(allocator);
rep = allocator.makeArray!ubyte(value.length);
value.rep.get.copy(rep.get);
} }
~this() /// Ditto.
this(IAllocator allocator)
{ {
destroy(rep); this.allocator = allocator;
rep = RefCounted!(ubyte[])(allocator);
} }
Integer opAssign(in uint value) /*
* Figure out the minimum amount of space this value will take
* up in bytes (leave at least one byte, though, if the value is 0).
*/
pragma(inline, true)
private ushort calculateSizeFromInt(in ref uint value)
const pure nothrow @safe @nogc
{ {
uint mask, shift;
ushort size = 4; ushort size = 4;
for (uint mask = 0xff000000; mask > 0x000000ff; mask >>= 8)
// Figure out the minimum amount of space this value will take
// up in bytes (leave at least one byte, though, if the value is 0).
for (mask = 0xff000000; mask > 0x000000ff; mask >>= 8)
{ {
if (value & mask) if (value & mask)
{ {
@ -57,26 +89,44 @@ struct Integer
} }
--size; --size;
} }
rep.length = size; return size;
}
// Work backward through the int, masking off each byte
// (up to the first 0 byte) and copy it into the internal /*
// representation in big-endian format. * Work backward through the int, masking off each byte
mask = 0x00000000ff; * (up to the first 0 byte) and copy it into the internal
shift = 0; * representation in big-endian format.
for (auto i = size; i; --i) */
pragma(inline, true)
private void assignInt(in ref ushort size, in ref uint value)
pure nothrow @safe @nogc
{
uint mask = 0x00000000ff, shift;
for (ushort i = size; i; --i)
{ {
rep[i - 1] = cast(ubyte) ((value & mask) >> shift); rep[i - 1] = cast(ubyte) ((value & mask) >> shift);
mask <<= 8; mask <<= 8;
shift += 8; shift += 8;
} }
}
ref Integer opAssign(in uint value)
{
ushort size = calculateSizeFromInt(value);
checkAllocator();
allocator.resizeArray(rep.get, size);
assignInt(size, value);
return this; return this;
} }
Integer opAssign(in Integer value) ref Integer opAssign(in Integer value)
{ {
rep.length = value.length; checkAllocator();
value.rep.copy(rep); allocator.resizeArray(rep, value.length);
value.rep.get.copy(rep.get);
return this; return this;
} }
@ -101,22 +151,42 @@ struct Integer
assert(h.rep[0] == 0); assert(h.rep[0] == 0);
} }
/**
* Returns: Integer size.
*/
@property size_t length() const pure nothrow @safe @nogc @property size_t length() const pure nothrow @safe @nogc
{ {
return rep.length; return rep.get.length;
} }
bool opEquals(in Integer h) /**
* Params:
* h = The second integer.
*
* Returns: Whether the two integers are equal.
*/
bool opEquals(in Integer h) const
{ {
return rep == h.rep; return rep == h.rep;
} }
///
unittest
{
auto h1 = Integer(1019);
assert(h1 == Integer(1019));
assert(h1 != Integer(109));
}
/** /**
* Compare h1 to h2. Return: * Params:
* a positive number if h1 > h2 * h = The second integer.
* a negative number if h1 < h2 *
* Returns: A positive number if $(D_INLINECODE this > h), a negative
* number if $(D_INLINECODE this > h), `0` otherwise.
*/ */
int opCmp(in Integer h) int opCmp(in Integer h) const
{ {
if (length > h.length) if (length > h.length)
{ {
@ -165,21 +235,27 @@ struct Integer
} }
/** /**
* Add two huges - overwrite h1 with the result. * Assignment operators with another $(D_PSYMBOL Integer).
*
* Params:
* h = The second integer.
*
* Returns: $(D_KEYWORD this).
*/ */
Integer opOpAssign(string op)(Integer h) ref Integer opOpAssign(string op)(in Integer h)
if (op == "+") if (op == "+")
{ {
uint sum; uint sum;
uint carry = 0; uint carry = 0;
checkAllocator();
// Adding h2 to h1. If h2 is > h1 to begin with, resize h1 // Adding h2 to h1. If h2 is > h1 to begin with, resize h1
if (h.length > length) if (h.length > length)
{ {
auto tmp = new ubyte[h.length]; auto tmp = allocator.makeArray!ubyte(h.length);
tmp[h.length - length ..$] = rep[0..length]; tmp[h.length - length .. $] = rep[0 .. length];
destroy(rep);
rep = tmp; rep = tmp;
} }
@ -206,10 +282,9 @@ struct Integer
if (carry) if (carry)
{ {
// Still overflowed; allocate more space // Still overflowed; allocate more space
ubyte[] tmp = new ubyte[length + 1]; auto tmp = allocator.makeArray!ubyte(length + 1);
tmp[1..$] = rep[0..length]; tmp[1..$] = rep[0..length];
tmp[0] = 0x01; tmp[0] = 0x01;
destroy(rep);
rep = tmp; rep = tmp;
} }
return this; return this;
@ -232,13 +307,16 @@ struct Integer
assert(h1.rep == [0x01, 0x00, 0x00, 0x11, 0x02]); assert(h1.rep == [0x01, 0x00, 0x00, 0x11, 0x02]);
} }
Integer opOpAssign(string op)(Integer h) /// Ditto.
ref Integer opOpAssign(string op)(in Integer h)
if (op == "-") if (op == "-")
{ {
auto i = rep.length; auto i = rep.length;
auto j = h.rep.length; auto j = h.rep.length;
uint borrow = 0; uint borrow = 0;
checkAllocator();
do do
{ {
int difference; int difference;
@ -271,10 +349,10 @@ struct Integer
immutable offset = rep.countUntil!(a => a != 0); immutable offset = rep.countUntil!(a => a != 0);
if (offset > 0) if (offset > 0)
{ {
ubyte[] tmp = rep; ubyte[] tmp;
rep = new ubyte[rep.length - offset]; allocator.resizeArray(tmp, rep.length - offset);
tmp[offset..$].copy(rep); rep[offset .. $].copy(tmp);
destroy(tmp); rep = tmp;
} }
return this; return this;
} }
@ -293,10 +371,10 @@ struct Integer
h2 = 4294967294; h2 = 4294967294;
h1 -= h2; h1 -= h2;
assert(h1.rep == [0x80, 0x00, 0x00, 0x01]); assert(h1.rep == [0x80, 0x00, 0x00, 0x01]);
} }
Integer opOpAssign(string op)(in size_t n) /// Ditto.
ref Integer opOpAssign(string op)(in size_t n)
if (op == "<<") if (op == "<<")
{ {
ubyte carry; ubyte carry;
@ -305,14 +383,15 @@ struct Integer
immutable bit = n % 8; immutable bit = n % 8;
immutable delta = 8 - bit; immutable delta = 8 - bit;
checkAllocator();
if (cast(ubyte) (rep[0] >> delta)) if (cast(ubyte) (rep[0] >> delta))
{ {
rep.length = rep.length + n / 8 + 1; allocator.resizeArray(rep, i + n / 8 + 1);
j = i + 1; j = i + 1;
} }
else else
{ {
rep.length = rep.length + n / 8; allocator.resizeArray(rep, i + n / 8);
j = i; j = i;
} }
do do
@ -339,13 +418,16 @@ struct Integer
assert(h1.rep == [0x01, 0xff, 0xff, 0xff, 0xfe]); assert(h1.rep == [0x01, 0xff, 0xff, 0xff, 0xfe]);
} }
Integer opOpAssign(string op)(in size_t n) /// Ditto.
ref Integer opOpAssign(string op)(in size_t n)
if (op == ">>") if (op == ">>")
{ {
immutable step = n / 8; immutable step = n / 8;
checkAllocator();
if (step >= rep.length) if (step >= rep.length)
{ {
rep.length = 1; allocator.resizeArray(rep, 1);
rep[0] = 0; rep[0] = 0;
return this; return this;
} }
@ -405,10 +487,8 @@ struct Integer
assert(h1.rep == [0x00]); assert(h1.rep == [0x00]);
} }
/** /// Ditto.
* Multiply h1 by h2, overwriting the value of h1. ref Integer opOpAssign(string op)(in Integer h)
*/
Integer opOpAssign(string op)(in Integer h)
if (op == "*") if (op == "*")
{ {
ubyte mask; ubyte mask;
@ -416,7 +496,6 @@ struct Integer
auto temp = Integer(this); auto temp = Integer(this);
opAssign(0); opAssign(0);
do do
{ {
--i; --i;
@ -443,16 +522,8 @@ struct Integer
assert(h1.rep == [0xdb, 0x18]); // 56088 assert(h1.rep == [0xdb, 0x18]); // 56088
} }
/** /// Ditto.
* divident = numerator, divisor = denominator ref Integer opOpAssign(string op)(in Integer h)
*
* Note that this process destroys divisor (and, of couse,
* overwrites quotient). The divident is the remainder of the
* division (if that's important to the caller). The divisor will
* be modified by this routine, but it will end up back where it
* "started".
*/
Integer opOpAssign(string op)(in Integer h)
if ((op == "/") || (op == "%")) if ((op == "/") || (op == "%"))
{ {
auto divisor = Integer(h); auto divisor = Integer(h);
@ -460,6 +531,8 @@ struct Integer
// is being set or cleared on the current operation. // is being set or cleared on the current operation.
size_t bit_size; size_t bit_size;
checkAllocator();
// First, left-shift divisor until it's >= than the divident // First, left-shift divisor until it's >= than the divident
while (opCmp(divisor) > 0) while (opCmp(divisor) > 0)
{ {
@ -468,7 +541,7 @@ struct Integer
} }
static if (op == "/") static if (op == "/")
{ {
auto quotient = new ubyte[bit_size / 8 + 1]; auto quotient = allocator.makeArray!ubyte(bit_size / 8 + 1);
} }
auto bit_position = 8 - (bit_size % 8) - 1; auto bit_position = 8 - (bit_size % 8) - 1;
@ -494,7 +567,6 @@ struct Integer
static if (op == "/") static if (op == "/")
{ {
destroy(rep);
rep = quotient; rep = quotient;
} }
return this; return this;
@ -522,7 +594,8 @@ struct Integer
assert(h1.rep == [0x7b]); // 123 assert(h1.rep == [0x7b]); // 123
} }
Integer opOpAssign(string op)(in Integer exp) /// Ditto.
ref Integer opOpAssign(string op)(in Integer exp)
if (op == "^^") if (op == "^^")
{ {
auto i = exp.rep.length; auto i = exp.rep.length;
@ -563,4 +636,6 @@ struct Integer
h1 ^^= h2; h1 ^^= h2;
assert(h1.rep == [0x1b, 0x5c, 0xab, 0x9c, 0x31, 0x10]); assert(h1.rep == [0x1b, 0x5c, 0xab, 0x9c, 0x31, 0x10]);
} }
mixin StructAllocator;
} }