summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-05-01 09:59:29 +0200
committerEugen Wissner <belka@caraus.de>2017-05-01 09:59:29 +0200
commit64e0d666ed34fffe05a6cf086fb096ebaa228f80 (patch)
treeece352dfb793af542a6e770155a93eba6125ffce
parentd629525a4b0b6568154ec642faf2596179600846 (diff)
parentf2aac680c588837a228155ce5e05eee6f30eeb24 (diff)
downloadtanya-64e0d666ed34fffe05a6cf086fb096ebaa228f80.tar.gz
Merge branch 'master' of github.com:caraus-ecms/tanya into utf8string
-rw-r--r--source/tanya/container/list.d14
-rw-r--r--source/tanya/container/package.d3
-rw-r--r--source/tanya/container/slice.d2549
-rw-r--r--source/tanya/container/string.d905
-rw-r--r--source/tanya/container/vector.d1642
-rw-r--r--source/tanya/math/mp.d1400
-rw-r--r--source/tanya/math/package.d9
7 files changed, 3395 insertions, 3127 deletions
diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d
index adbd4fd..d0946d9 100644
--- a/source/tanya/container/list.d
+++ b/source/tanya/container/list.d
@@ -215,16 +215,19 @@ struct SList(T)
* If $(D_PARAM init) is passed by reference, it will be copied.
*
* Params:
+ * R = Source list type.
* init = Source list.
* allocator = Allocator.
*/
- this(ref SList init, shared Allocator allocator = defaultAllocator)
+ this(R)(ref R init, shared Allocator allocator = defaultAllocator)
+ if (is(Unqual!R == SList))
{
this(init[], allocator);
}
/// Ditto.
- this(SList init, shared Allocator allocator = defaultAllocator) @trusted
+ this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
+ if (is(R == SList))
{
this(allocator);
if (allocator is init.allocator)
@@ -734,18 +737,19 @@ struct SList(T)
*
* Returns: $(D_KEYWORD this).
*/
- ref typeof(this) opAssign(R)(const ref R that)
+ ref typeof(this) opAssign(R)(ref R that)
if (is(Unqual!R == SList))
{
return this = that[];
}
/// Ditto.
- ref typeof(this) opAssign(R)(const ref R that)
- if (is(Unqual!R == SList))
+ ref typeof(this) opAssign(R)(R that)
+ if (is(R == SList))
{
swap(this.head, that.head);
swap(this.allocator_, that.allocator_);
+ return this;
}
/**
diff --git a/source/tanya/container/package.d b/source/tanya/container/package.d
index 171f660..35400d5 100644
--- a/source/tanya/container/package.d
+++ b/source/tanya/container/package.d
@@ -14,5 +14,6 @@ module tanya.container;
public import tanya.container.buffer;
public import tanya.container.list;
-public import tanya.container.slice;
+public import tanya.container.string;
+public import tanya.container.vector;
public import tanya.container.queue;
diff --git a/source/tanya/container/slice.d b/source/tanya/container/slice.d
deleted file mode 100644
index 321acfb..0000000
--- a/source/tanya/container/slice.d
+++ /dev/null
@@ -1,2549 +0,0 @@
-/* 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/. */
-
-/**
- * Single-dimensioned array.
- *
- * Copyright: Eugene Wissner 2016-2017.
- * 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)
- */
-module tanya.container.slice;
-
-import core.checkedint;
-import core.exception;
-import std.algorithm.comparison;
-import std.algorithm.mutation;
-import std.conv;
-import std.range.primitives;
-import std.meta;
-import std.traits;
-import tanya.memory;
-
-/**
- * Random-access range for the $(D_PSYMBOL Vector).
- *
- * Params:
- * T = Element type.
- */
-struct Slice(T)
- if (!is(Unqual!T == char))
-{
- private T* begin, end;
- private alias ContainerType = CopyConstness!(T, Vector!(Unqual!T));
- private ContainerType* vector;
-
- invariant
- {
- assert(this.begin <= this.end);
- assert(this.vector !is null);
- assert(this.begin >= this.vector.data);
- assert(this.end <= this.vector.data + this.vector.length);
- }
-
- private this(ref ContainerType vector, T* begin, T* end) @trusted
- in
- {
- assert(begin <= end);
- assert(begin >= vector.data);
- assert(end <= vector.data + vector.length);
- }
- body
- {
- this.vector = &vector;
- this.begin = begin;
- this.end = end;
- }
-
- @disable this();
-
- @property Slice save()
- {
- return this;
- }
-
- @property bool empty() const
- {
- return this.begin == this.end;
- }
-
- @property size_t length() const
- {
- return this.end - this.begin;
- }
-
- alias opDollar = length;
-
- @property ref inout(T) front() inout
- in
- {
- assert(!empty);
- }
- body
- {
- return *this.begin;
- }
-
- @property ref inout(T) back() inout @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- return *(this.end - 1);
- }
-
- void popFront() @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- ++this.begin;
- }
-
- void popBack() @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- --this.end;
- }
-
- ref inout(T) opIndex(const size_t i) inout @trusted
- in
- {
- assert(i < length);
- }
- body
- {
- return *(this.begin + i);
- }
-
- Slice opIndex()
- {
- return typeof(return)(*this.vector, this.begin, this.end);
- }
-
- Slice!(const T) opIndex() const
- {
- return typeof(return)(*this.vector, this.begin, this.end);
- }
-
- Slice opSlice(const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(*this.vector, this.begin + i, this.begin + j);
- }
-
- Slice!(const T) opSlice(const size_t i, const size_t j) const @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(*this.vector, this.begin + i, this.begin + j);
- }
-
- inout(T[]) get() inout @trusted
- {
- return this.begin[0 .. length];
- }
-}
-
-/**
- * One dimensional array.
- *
- * Params:
- * T = Content type.
- */
-struct Vector(T)
- if (!is(T == char))
-{
- private size_t length_;
- private T* data;
- private size_t capacity_;
-
- invariant
- {
- assert(this.length_ <= this.capacity_);
- assert(this.capacity_ == 0 || this.data !is null);
- }
-
- /**
- * Creates a new $(D_PSYMBOL Vector) with the elements from a static array.
- *
- * Params:
- * R = Static array size.
- * init = Values to initialize the vector with.
- * allocator = Allocator.
- */
- this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
- {
- this(allocator);
- insertBack!(T[])(init[]);
- }
-
- /**
- * Creates a new $(D_PSYMBOL Vector) with the elements from an input range.
- *
- * Params:
- * R = Type of the initial range.
- * init = Values to initialize the vector with.
- * allocator = Allocator.
- */
- this(R)(R init, shared Allocator allocator = defaultAllocator)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- this(allocator);
- insertBack(init);
- }
-
- /**
- * Initializes this vector from another one.
- *
- * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
- * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
- * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
- * storage, otherwise, the storage will be allocated with
- * $(D_PARAM allocator) and all elements will be moved;
- * $(D_PARAM init) will be destroyed at the end.
- *
- * If $(D_PARAM init) is passed by reference, it will be copied.
- *
- * Params:
- * R = Vector type.
- * init = Source vector.
- * allocator = Allocator.
- */
- this(R)(const ref R init, shared Allocator allocator = defaultAllocator)
- if (is(Unqual!R == Vector))
- {
- this(allocator);
- insertBack(init[]);
- }
-
- /// Ditto.
- this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
- if (is(R == Vector))
- {
- this(allocator);
- if (allocator is init.allocator)
- {
- // Just steal all references and the allocator.
- this.data = init.data;
- this.length_ = init.length_;
- this.capacity_ = init.capacity_;
-
- // Reset the source vector, so it can't destroy the moved storage.
- init.length_ = init.capacity_ = 0;
- init.data = null;
- }
- else
- {
- // Move each element.
- reserve(init.length_);
- moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]);
- this.length_ = init.length_;
- // Destructor of init should destroy it here.
- }
- }
-
- ///
- @trusted @nogc unittest
- {
- auto v1 = Vector!int([1, 2, 3]);
- auto v2 = Vector!int(v1);
- assert(v1 == v2);
-
- auto v3 = Vector!int(Vector!int([1, 2, 3]));
- assert(v1 == v3);
- assert(v3.length == 3);
- assert(v3.capacity == 3);
- }
-
- private @trusted @nogc unittest // const constructor tests
- {
- auto v1 = const Vector!int([1, 2, 3]);
- auto v2 = Vector!int(v1);
- assert(v1.data !is v2.data);
- assert(v1 == v2);
-
- auto v3 = const Vector!int(Vector!int([1, 2, 3]));
- assert(v1 == v3);
- assert(v3.length == 3);
- assert(v3.capacity == 3);
- }
-
- /**
- * Creates a new $(D_PSYMBOL Vector).
- *
- * Params:
- * len = Initial length of the vector.
- * init = Initial value to fill the vector with.
- * allocator = Allocator.
- */
- this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
- {
- this(allocator);
- reserve(len);
- uninitializedFill(this.data[0 .. len], init);
- length_ = len;
- }
-
- /// Ditto.
- this(const size_t len, shared Allocator allocator = defaultAllocator)
- {
- this(allocator);
- length = len;
- }
-
- /// Ditto.
- this(shared Allocator allocator)
- in
- {
- assert(allocator !is null);
- }
- body
- {
- allocator_ = allocator;
- }
-
- ///
- unittest
- {
- auto v = Vector!int([3, 8, 2]);
-
- assert(v.capacity == 3);
- assert(v.length == 3);
- assert(v[0] == 3 && v[1] == 8 && v[2] == 2);
- }
-
- ///
- unittest
- {
- auto v = Vector!int(3, 5);
-
- assert(v.capacity == 3);
- assert(v.length == 3);
- assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
- }
-
- @safe unittest
- {
- auto v1 = Vector!int(defaultAllocator);
- }
-
- /**
- * Destroys this $(D_PSYMBOL Vector).
- */
- ~this() @trusted
- {
- clear();
- allocator.deallocate(this.data[0 .. capacity]);
- }
-
- /**
- * Copies the vector.
- */
- this(this)
- {
- auto buf = this.data[0 .. this.length_];
- this.length_ = capacity_ = 0;
- this.data = null;
- insertBack(buf);
- }
-
- /**
- * Removes all elements.
- */
- void clear()
- {
- length = 0;
- }
-
- ///
- unittest
- {
- auto v = Vector!int([18, 20, 15]);
- v.clear();
- assert(v.length == 0);
- assert(v.capacity == 3);
- }
-
- /**
- * Returns: How many elements the vector can contain without reallocating.
- */
- @property size_t capacity() const
- {
- return capacity_;
- }
-
- ///
- @safe @nogc unittest
- {
- auto v = Vector!int(4);
- assert(v.capacity == 4);
- }
-
- /**
- * Returns: Vector length.
- */
- @property size_t length() const
- {
- return length_;
- }
-
- /// Ditto.
- size_t opDollar() const
- {
- return length;
- }
-
- /**
- * Expands/shrinks the vector.
- *
- * Params:
- * len = New length.
- */
- @property void length(const size_t len) @trusted
- {
- if (len == length)
- {
- return;
- }
- else if (len > length)
- {
- reserve(len);
- initializeAll(this.data[length_ .. len]);
- }
- else
- {
- static if (hasElaborateDestructor!T)
- {
- const T* end = this.data + length_ - 1;
- for (T* e = this.data + len; e != end; ++e)
- {
- destroy(*e);
- }
- }
- }
- length_ = len;
- }
-
- ///
- unittest
- {
- Vector!int v;
-
- v.length = 5;
- assert(v.length == 5);
- assert(v.capacity == 5);
-
- v.length = 7;
- assert(v.length == 7);
- assert(v.capacity == 7);
-
- assert(v[$ - 1] == 0);
- v[$ - 1] = 3;
- assert(v[$ - 1] == 3);
-
- v.length = 0;
- assert(v.length == 0);
- assert(v.capacity == 7);
- }
-
- /**
- * Reserves space for $(D_PARAM size) elements.
- *
- * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the
- * function call does not cause a reallocation and the vector capacity is not
- * affected.
- *
- * Params:
- * size = Desired size.
- */
- void reserve(const size_t size) @trusted
- {
- if (capacity_ >= size)
- {
- return;
- }
- bool overflow;
- immutable byteSize = mulu(size, T.sizeof, overflow);
- assert(!overflow);
-
- void[] buf = this.data[0 .. this.capacity_];
- if (!allocator.reallocateInPlace(buf, byteSize))
- {
- buf = allocator.allocate(byteSize);
- if (buf is null)
- {
- onOutOfMemoryErrorNoGC();
- }
- scope (failure)
- {
- allocator.deallocate(buf);
- }
- const T* end = this.data + this.length_;
- for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
- {
- moveEmplace(*src, *dest);
- static if (hasElaborateDestructor!T)
- {
- destroy(*src);
- }
- }
- allocator.deallocate(this.data[0 .. this.capacity_]);
- this.data = cast(T*) buf;
- }
- this.capacity_ = size;
- }
-
- ///
- @nogc @safe unittest
- {
- Vector!int v;
- assert(v.capacity == 0);
- assert(v.length == 0);
-
- v.reserve(3);
- assert(v.capacity == 3);
- assert(v.length == 0);
- }
-
- /**
- * Requests the vector to reduce its capacity to fit the $(D_PARAM size).
- *
- * The request is non-binding. The vector won't become smaller than the
- * $(D_PARAM length).
- *
- * Params:
- * size = Desired size.
- */
- void shrink(const size_t size) @trusted
- {
- if (capacity <= size)
- {
- return;
- }
- immutable n = max(length, size);
- void[] buf = this.data[0 .. this.capacity_];
- if (allocator.reallocateInPlace(buf, n * T.sizeof))
- {
- this.capacity_ = n;
- }
- }
-
- ///
- @nogc @safe unittest
- {
- Vector!int v;
- assert(v.capacity == 0);
- assert(v.length == 0);
-
- v.reserve(5);
- v.insertBack(1);
- v.insertBack(3);
- assert(v.capacity == 5);
- assert(v.length == 2);
- }
-
- /**
- * Returns: $(D_KEYWORD true) if the vector is empty.
- */
- @property bool empty() const
- {
- return length == 0;
- }
-
- /**
- * Removes the value at the back of the vector.
- *
- * Returns: The number of elements removed
- *
- * Precondition: $(D_INLINECODE !empty).
- */
- void removeBack()
- in
- {
- assert(!empty);
- }
- body
- {
- length = length - 1;
- }
-
- /**
- * Removes $(D_PARAM howMany) elements from the vector.
- *
- * This method doesn't fail if it could not remove $(D_PARAM howMany)
- * elements. Instead, if $(D_PARAM howMany) is greater than the vector
- * length, all elements are removed.
- *
- * Params:
- * howMany = How many elements should be removed.
- *
- * Returns: The number of elements removed
- */
- size_t removeBack(const size_t howMany)
- out (removed)
- {
- assert(removed <= howMany);
- }
- body
- {
- immutable toRemove = min(howMany, length);
-
- length = length - toRemove;
-
- return toRemove;
- }
-
- ///
- unittest
- {
- auto v = Vector!int([5, 18, 17]);
-
- assert(v.removeBack(0) == 0);
- assert(v.removeBack(2) == 2);
- assert(v.removeBack(3) == 1);
- assert(v.removeBack(3) == 0);
- }
-
- /**
- * Remove all elements beloning to $(D_PARAM r).
- *
- * Params:
- * r = Range originally obtained from this vector.
- *
- * Returns: A range spanning the remaining elements in the array that
- * initially were right after $(D_PARAM r).
- *
- * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
- */
- Slice!T remove(Slice!T r) @trusted
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- auto end = this.data + this.length;
- moveAll(Slice!T(this, r.end, end), Slice!T(this, r.begin, end));
- length = length - r.length;
- return Slice!T(this, r.begin, this.data + length);
- }
-
- ///
- @safe @nogc unittest
- {
- auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]);
-
- assert(v.remove(v[1 .. 3]).length == 4);
- assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
- assert(v.length == 5);
-
- assert(v.remove(v[4 .. 4]).length == 1);
- assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
- assert(v.length == 5);
-
- assert(v.remove(v[4 .. 5]).length == 0);
- assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6);
- assert(v.length == 4);
-
- assert(v.remove(v[]).length == 0);
-
- }
-
- private void moveBack(R)(ref R el) @trusted
- if (isImplicitlyConvertible!(R, T))
- {
- reserve(this.length + 1);
- moveEmplace(el, *(this.data + this.length_));
- ++this.length_;
- }
-
- /**
- * Inserts the $(D_PARAM el) into the vector.
- *
- * Params:
- * R = Type of the inserted value(s) (single value, range or static array).
- * el = Value(s) should be inserted.
- *
- * Returns: The number of elements inserted.
- */
- size_t insertBack(R)(R el)
- if (isImplicitlyConvertible!(R, T))
- {
- moveBack(el);
- return 1;
- }
-
- /// Ditto.
- size_t insertBack(R)(ref R el) @trusted
- if (isImplicitlyConvertible!(R, T))
- {
- reserve(this.length_ + 1);
- emplace(this.data + this.length_, el);
- ++this.length_;
- return 1;
- }
-
- /// Ditto.
- size_t insertBack(R)(R el)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- static if (hasLength!R)
- {
- reserve(length + el.length);
- }
- size_t retLength;
- foreach (e; el)
- {
- retLength += insertBack(e);
- }
- return retLength;
- }
-
- /// Ditto.
- size_t insertBack(size_t R)(T[R] el)
- {
- return insertBack!(T[])(el[]);
- }
-
- /// Ditto.
- alias insert = insertBack;
-
- ///
- unittest
- {
- struct TestRange
- {
- int counter = 6;
-
- int front()
- {
- return counter;
- }
-
- void popFront()
- {
- counter -= 2;
- }
-
- bool empty()
- {
- return counter == 0;
- }
- }
-
- Vector!int v1;
-
- assert(v1.insertBack(5) == 1);
- assert(v1.length == 1);
- assert(v1.capacity == 1);
- assert(v1.back == 5);
-
- assert(v1.insertBack(TestRange()) == 3);
- assert(v1.length == 4);
- assert(v1.capacity == 4);
- assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2);
-
- assert(v1.insertBack([34, 234]) == 2);
- assert(v1.length == 6);
- assert(v1.capacity == 6);
- assert(v1[4] == 34 && v1[5] == 234);
- }
-
- /**
- * Inserts $(D_PARAM el) before or after $(D_PARAM r).
- *
- * Params:
- * R = Type of the inserted value(s) (single value, range or static array).
- * r = Range originally obtained from this vector.
- * el = Value(s) should be inserted.
- *
- * Returns: The number of elements inserted.
- *
- * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
- */
- size_t insertAfter(R)(Slice!T r, R el)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- immutable oldLen = length;
- immutable offset = r.end - this.data;
- immutable inserted = insertBack(el);
- bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
- return inserted;
- }
-
- /// Ditto.
- size_t insertAfter(size_t R)(Slice!T r, T[R] el)
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- return insertAfter!(T[])(r, el[]);
- }
-
- /// Ditto.
- size_t insertAfter(R)(Slice!T r, auto ref R el)
- if (isImplicitlyConvertible!(R, T))
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- immutable oldLen = length;
- immutable offset = r.end - this.data;
-
- static if (__traits(isRef, el))
- {
- insertBack(el);
- }
- else
- {
- moveBack(el);
- }
- bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
-
- return 1;
- }
-
- /// Ditto.
- size_t insertBefore(R)(Slice!T r, R el)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- return insertAfter(Slice!T(this, this.data, r.begin), el);
- }
-
- /// Ditto.
- size_t insertBefore(size_t R)(Slice!T r, T[R] el)
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- return insertBefore!(T[])(r, el[]);
- }
-
- /// Ditto.
- size_t insertBefore(R)(Slice!T r, auto ref R el)
- if (isImplicitlyConvertible!(R, T))
- in
- {
- assert(r.vector is &this);
- assert(r.begin >= this.data);
- assert(r.end <= this.data + length);
- }
- body
- {
- immutable oldLen = length;
- immutable offset = r.begin - this.data;
-
- static if (__traits(isRef, el))
- {
- insertBack(el);
- }
- else
- {
- moveBack(el);
- }
- bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
-
- return 1;
- }
-
- ///
- unittest
- {
- Vector!int v1;
- v1.insertAfter(v1[], [2, 8]);
- assert(v1[0] == 2);
- assert(v1[1] == 8);
- assert(v1.length == 2);
-
- v1.insertAfter(v1[], [1, 2]);
- assert(v1[0] == 2);
- assert(v1[1] == 8);
- assert(v1[2] == 1);
- assert(v1[3] == 2);
- assert(v1.length == 4);
-
- v1.insertAfter(v1[0 .. 0], [1, 2]);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 2);
- assert(v1[3] == 8);
- assert(v1[4] == 1);
- assert(v1[5] == 2);
- assert(v1.length == 6);
-
- v1.insertAfter(v1[0 .. 4], 9);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 2);
- assert(v1[3] == 8);
- assert(v1[4] == 9);
- assert(v1[5] == 1);
- assert(v1[6] == 2);
- assert(v1.length == 7);
- }
-
- ///
- unittest
- {
- Vector!int v1;
- v1.insertBefore(v1[], [2, 8]);
- assert(v1[0] == 2);
- assert(v1[1] == 8);
- assert(v1.length == 2);
-
- v1.insertBefore(v1[], [1, 2]);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 2);
- assert(v1[3] == 8);
- assert(v1.length == 4);
-
- v1.insertBefore(v1[0 .. 1], [1, 2]);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 1);
- assert(v1[3] == 2);
- assert(v1[4] == 2);
- assert(v1[5] == 8);
- assert(v1.length == 6);
-
- v1.insertBefore(v1[2 .. $], 9);
- assert(v1[0] == 1);
- assert(v1[1] == 2);
- assert(v1[2] == 9);
- assert(v1[3] == 1);
- assert(v1[4] == 2);
- assert(v1[5] == 2);
- assert(v1[6] == 8);
- assert(v1.length == 7);
- }
-
- /**
- * Assigns a value to the element with the index $(D_PARAM pos).
- *
- * Params:
- * value = Value.
- * pos = Position.
- *
- * Returns: Assigned value.
- *
- * Precondition: $(D_INLINECODE length > pos).
- */
- ref T opIndexAssign(ref T value, const size_t pos)
- {
- return opIndex(pos) = value;
- }
-
- @safe unittest
- {
- Vector!int a = Vector!int(1);
- a[0] = 5;
- assert(a[0] == 5);
- }
-
- /// Ditto.
- T opIndexAssign(T value, const size_t pos)
- {
- return opIndexAssign(value, pos);
- }
-
- /// Ditto.
- Slice!T opIndexAssign(T value)
- {
- return opSliceAssign(value, 0, length);
- }
-
- /// Ditto.
- Slice!T opIndexAssign(ref T value)
- {
- return opSliceAssign(value, 0, length);
- }
-
- /**
- * Assigns a range or a static array.
- *
- * Params:
- * R = Range type or static array length.
- * value = Value.
- *
- * Returns: Assigned value.
- *
- * Precondition: $(D_INLINECODE length == value.length).
- */
- Slice!T opIndexAssign(R)(R value)
- if (!isInfinite!R && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- return opSliceAssign!R(value, 0, length);
- }
-
- /// Ditto.
- Slice!T opIndexAssign(size_t R)(T[R] value)
- {
- return opSliceAssign!R(value, 0, length);
- }
-
- ///
- @nogc unittest
- {
- auto v1 = Vector!int([12, 1, 7]);
-
- v1[] = 3;
- assert(v1[0] == 3);
- assert(v1[1] == 3);
- assert(v1[2] == 3);
-
- v1[] = [7, 1, 12];
- assert(v1[0] == 7);
- assert(v1[1] == 1);
- assert(v1[2] == 12);
- }
-
- /**
- * Params:
- * pos = Index.
- *
- * Returns: The value at a specified index.
- *
- * Precondition: $(D_INLINECODE length > pos).
- */
- ref inout(T) opIndex(const size_t pos) inout @trusted
- in
- {
- assert(length > pos);
- }
- body
- {
- return *(this.data + pos);
- }
-
- /**
- * Returns: Random access range that iterates over elements of the vector, in
- * forward order.
- */
- Slice!T opIndex() @trusted
- {
- return typeof(return)(this, this.data, this.data + length);
- }
-
- /// Ditto.
- Slice!(const T) opIndex() const @trusted
- {
- return typeof(return)(this, this.data, this.data + length);
- }
-
- ///
- unittest
- {
- const v1 = Vector!int([6, 123, 34, 5]);
-
- assert(v1[0] == 6);
- assert(v1[1] == 123);
- assert(v1[2] == 34);
- assert(v1[3] == 5);
- static assert(is(typeof(v1[0]) == const(int)));
- static assert(is(typeof(v1[])));
- }
-
- /**
- * Comparison for equality.
- *
- * Params:
- * that = The vector to compare with.
- *
- * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false)
- * otherwise.
- */
- bool opEquals()(auto ref typeof(this) that) @trusted
- {
- return equal(this.data[0 .. length], that.data[0 .. that.length]);
- }
-
- /// Ditto.
- bool opEquals()(const auto ref typeof(this) that) const @trusted
- {
- return equal(this.data[0 .. length], that.data[0 .. that.length]);
- }
-
- /// Ditto.
- bool opEquals(Slice!T that)
- {
- return equal(opIndex(), that);
- }
-
- /**
- * Comparison for equality.
- *
- * Params:
- * R = Right hand side type.
- * that = Right hand side vector range.
- *
- * Returns: $(D_KEYWORD true) if the vector and the range are equal,
- * $(D_KEYWORD false) otherwise.
- */
- bool opEquals(R)(Slice!R that) const
- if (is(Unqual!R == T))
- {
- return equal(opIndex(), that);
- }
-
- ///
- unittest
- {
- Vector!int v1, v2;
- assert(v1 == v2);
-
- v1.length = 1;
- v2.length = 2;
- assert(v1 != v2);
-
- v1.length = 2;
- v1[0] = v2[0] = 2;
- v1[1] = 3;
- v2[1] = 4;
- assert(v1 != v2);
-
- v2[1] = 3;
- assert(v1 == v2);
- }
-
- /**
- * Returns: The first element.
- *
- * Precondition: $(D_INLINECODE !empty).
- */
- @property ref inout(T) front() inout
- in
- {
- assert(!empty);
- }
- body
- {
- return *this.data;
- }
-
- ///
- @safe unittest
- {
- auto v = Vector!int([5]);
-
- assert(v.front == 5);
-
- v.length = 2;
- v[1] = 15;
- assert(v.front == 5);
- }
-
- /**
- * Returns: The last element.
- *
- * Precondition: $(D_INLINECODE !empty).
- */
- @property ref inout(T) back() inout @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- return *(this.data + length - 1);
- }
-
- ///
- unittest
- {
- auto v = Vector!int([5]);
-
- assert(v.back == 5);
-
- v.length = 2;
- v[1] = 15;
- assert(v.back == 15);
- }
-
- /**
- * Params:
- * i = Slice start.
- * j = Slice end.
- *
- * Returns: A range that iterates over elements of the container from
- * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
- *
- * Precondition: $(D_INLINECODE i <= j && j <= length).
- */
- Slice!T opSlice(const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(this, this.data + i, this.data + j);
- }
-
- /// Ditto.
- Slice!(const T) opSlice(const size_t i, const size_t j) const @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(this, this.data + i, this.data + j);
- }
-
- ///
- unittest
- {
- Vector!int v;
- auto r = v[];
- assert(r.length == 0);
- assert(r.empty);
- }
-
- ///
- unittest
- {
- auto v = Vector!int([1, 2, 3]);
- auto r = v[];
-
- assert(r.front == 1);
- assert(r.back == 3);
-
- r.popFront();
- assert(r.front == 2);
-
- r.popBack();
- assert(r.back == 2);
-
- assert(r.length == 1);
- }
-
- ///
- unittest
- {
- auto v = Vector!int([1, 2, 3, 4]);
- auto r = v[1 .. 4];
- assert(r.length == 3);
- assert(r[0] == 2);
- assert(r[1] == 3);
- assert(r[2] == 4);
-
- r = v[0 .. 0];
- assert(r.length == 0);
-
- r = v[4 .. 4];
- assert(r.length == 0);
- }
-
- /**
- * Slicing assignment.
- *
- * Params:
- * R = Type of the assigned slice or length of the static array should be
- * assigned.
- * value = New value (single value, input range or static array).
- * i = Slice start.
- * j = Slice end.
- *
- * Returns: Slice with the assigned part of the vector.
- *
- * Precondition: $(D_INLINECODE i <= j && j <= length
- * && value.length == j - i)
- */
- Slice!T opSliceAssign(R)(R value, const size_t i, const size_t j) @trusted
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- in
- {
- assert(i <= j);
- assert(j <= length);
- assert(j - i == walkLength(value));
- }
- body
- {
- copy(value, this.data[i .. j]);
- return opSlice(i, j);
- }
-
- /// Ditto.
- Slice!T opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
- {
- return opSliceAssign!(T[])(value[], i, j);
- }
-
- /// Ditto.
- Slice!T opSliceAssign(ref T value, const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- fill(this.data[i .. j], value);
- return opSlice(i, j);
- }
-
- /// Ditto.
- Slice!T opSliceAssign(T value, const size_t i, const size_t j)
- {
- return opSliceAssign(value, i, j);
- }
-
- ///
- @nogc @safe unittest
- {
- auto v1 = Vector!int([3, 3, 3]);
- auto v2 = Vector!int([1, 2]);
-
- v1[0 .. 2] = 286;
- assert(v1[0] == 286);
- assert(v1[1] == 286);
- assert(v1[2] == 3);
-
- v2[0 .. $] = v1[1 .. 3];
- assert(v2[0] == 286);
- assert(v2[1] == 3);
-
- v1[0 .. 2] = [5, 8];
- assert(v1[0] == 5);
- assert(v1[1] == 8);
- assert(v1[2] == 3);
- }
-
- /**
- * Returns an array used internally by the vector to store its owned elements.
- * The length of the returned array may differ from the size of the allocated
- * memory for the vector: the array contains only initialized elements, but
- * not the reserved memory.
- *
- * Returns: The array with elements of this vector.
- */
- inout(T[]) get() inout @trusted
- {
- return this.data[0 .. length];
- }
-
- ///
- unittest
- {
- auto v = Vector!int([1, 2, 4]);
- auto data = v.get();
-
- assert(data[0] == 1);
- assert(data[1] == 2);
- assert(data[2] == 4);
- assert(data.length == 3);
-
- data = v[1 .. 2].get();
- assert(data[0] == 2);
- assert(data.length == 1);
- }
-
- /**
- * Assigns another vector.
- *
- * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
- * This vector will take the ownership over $(D_PARAM that)'s storage and
- * the allocator.
- *
- * If $(D_PARAM that) is passed by reference, it will be copied.
- *
- * Params:
- * R = Content type.
- * that = The value should be assigned.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign(R)(const ref R that)
- if (is(Unqual!R == Vector))
- {
- return this = that[];
- }
-
- /// Ditto.
- ref typeof(this) opAssign(R)(R that) @trusted
- if (is(R == Vector))
- {
- swap(this.data, that.data);
- swap(this.length_, that.length_);
- swap(this.capacity_, that.capacity_);
- swap(this.allocator_, that.allocator_);
- return this;
- }
-
- /**
- * Assigns a range to the vector.
- *
- * Params:
- * R = Content type.
- * that = The value should be assigned.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign(R)(R that)
- if (!isInfinite!R
- && isInputRange!R
- && isImplicitlyConvertible!(ElementType!R, T))
- {
- length = 0;
- insertBack(that);
- return this;
- }
-
- ///
- @safe @nogc unittest
- {
- auto v1 = const Vector!int([5, 15, 8]);
- Vector!int v2;
- v2 = v1;
- assert(v1 == v2);
- }
-
- ///
- @safe @nogc unittest
- {
- auto v1 = const Vector!int([5, 15, 8]);
- Vector!int v2;
- v2 = v1[0 .. 2];
- assert(equal(v1[0 .. 2], v2[]));
- }
-
- // Move assignment.
- private @safe @nogc unittest
- {
- Vector!int v1;
- v1 = Vector!int([5, 15, 8]);
- }
-
- /**
- * Assigns a static array.
- *
- * Params:
- * R = Static array size.
- * that = Values to initialize the vector with.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign(size_t R)(T[R] that)
- {
- return opAssign!(T[])(that[]);
- }
-
- ///
- @safe @nogc unittest
- {
- auto v1 = Vector!int([5, 15, 8]);
- Vector!int v2;
-
- v2 = [5, 15, 8];
- assert(v1 == v2);
- }
-
- mixin DefaultAllocator;
-}
-
-///
-unittest
-{
- auto v = Vector!int([5, 15, 8]);
-
- assert(v.front == 5);
- assert(v[1] == 15);
- assert(v.back == 8);
-
- auto r = v[];
- r[0] = 7;
- assert(r.front == 7);
- assert(r.front == v.front);
-}
-
-@nogc unittest
-{
- const v1 = Vector!int();
- const Vector!int v2;
- const v3 = Vector!int([1, 5, 8]);
- static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
-}
-
-@nogc unittest
-{
- // Test that const vectors return usable ranges.
- auto v = const Vector!int([1, 2, 4]);
- auto r1 = v[];
-
- assert(r1.back == 4);
- r1.popBack();
- assert(r1.back == 2);
- r1.popBack();
- assert(r1.back == 1);
- r1.popBack();
- assert(r1.length == 0);
-
- static assert(!is(typeof(r1[0] = 5)));
- static assert(!is(typeof(v[0] = 5)));
-
- const r2 = r1[];
- static assert(is(typeof(r2[])));
-}
-
-@nogc unittest
-{
- Vector!int v1;
- const Vector!int v2;
-
- auto r1 = v1[];
- auto r2 = v1[];
-
- assert(r1.length == 0);
- assert(r2.empty);
- assert(r1 == r2);
-
- v1.insertBack([1, 2, 4]);
- assert(v1[] == v1);
- assert(v2[] == v2);
- assert(v2[] != v1);
- assert(v1[] != v2);
- assert(v1[].equal(v1[]));
- assert(v2[].equal(v2[]));
- assert(!v1[].equal(v2[]));
-}
-
-@nogc unittest
-{
- struct MutableEqualsStruct
- {
- int opEquals(typeof(this) that) @nogc
- {
- return true;
- }
- }
- struct ConstEqualsStruct
- {
- int opEquals(const typeof(this) that) const @nogc
- {
- return true;
- }
- }
- auto v1 = Vector!ConstEqualsStruct();
- auto v2 = Vector!ConstEqualsStruct();
- assert(v1 == v2);
- assert(v1[] == v2);
- assert(v1 == v2[]);
- assert(v1[].equal(v2[]));
-
- auto v3 = const Vector!ConstEqualsStruct();
- auto v4 = const Vector!ConstEqualsStruct();
- assert(v3 == v4);
- assert(v3[] == v4);
- assert(v3 == v4[]);
- assert(v3[].equal(v4[]));
-
- auto v7 = Vector!MutableEqualsStruct(1, MutableEqualsStruct());
- auto v8 = Vector!MutableEqualsStruct(1, MutableEqualsStruct());
- assert(v7 == v8);
- assert(v7[] == v8);
- assert(v7 == v8[]);
- assert(v7[].equal(v8[]));
-}
-
-@nogc unittest
-{
- struct SWithDtor
- {
- ~this() @nogc
- {
- }
- }
- auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors.
-}
-
-private unittest
-{
- class A
- {
- }
- A a1, a2;
- auto v1 = Vector!A([a1, a2]);
-}
-
-private @safe @nogc unittest
-{
- auto v = Vector!int([5, 15, 8]);
- {
- size_t i;
-
- foreach (e; v)
- {
- assert(i != 0 || e == 5);
- assert(i != 1 || e == 15);
- assert(i != 2 || e == 8);
- ++i;
- }
- assert(i == 3);
- }
- {
- size_t i = 3;
-
- foreach_reverse (e; v)
- {
- --i;
- assert(i != 2 || e == 8);
- assert(i != 1 || e == 15);
- assert(i != 0 || e == 5);
- }
- assert(i == 0);
- }
-}
-
-private ref const(wchar) front(const wchar[] str)
-pure nothrow @safe @nogc
-in
-{
- assert(str.length > 0);
-}
-body
-{
- return str[0];
-}
-
-private void popFront(ref const(wchar)[] str, const size_t s = 1)
-pure nothrow @safe @nogc
-in
-{
- assert(str.length >= s);
-}
-body
-{
- str = str[s .. $];
-}
-
-/**
- * Thrown on encoding errors.
- */
-class UTFException : Exception
-{
- /**
- * Params:
- * msg = The message for the exception.
- * file = The file where the exception occurred.
- * line = The line number where the exception occurred.
- * next = The previous exception in the chain of exceptions, if any.
- */
- this(string msg,
- string file = __FILE__,
- size_t line = __LINE__,
- Throwable next = null) @nogc @safe pure nothrow
- {
- super(msg, file, line, next);
- }
-}
-
-/**
- * Iterates $(D_PSYMBOL String) by UTF-8 code unit.
- *
- * Params:
- * E = Element type ($(D_KEYWORD char) or $(D_INLINECODE const(char))).
- */
-struct ByCodeUnit(E)
- if (is(Unqual!E == char))
-{
- private E* begin, end;
- private alias ContainerType = CopyConstness!(E, Slice!char);
- private ContainerType* container;
-
- invariant
- {
- assert(this.begin <= this.end);
- assert(this.container !is null);
- assert(this.begin >= this.container.data);
- assert(this.end <= this.container.data + this.container.length);
- }
-
- private this(ref ContainerType container, E* begin, E* end) @trusted
- in
- {
- assert(begin <= end);
- assert(begin >= container.data);
- assert(end <= container.data + container.length);
- }
- body
- {
- this.container = &container;
- this.begin = begin;
- this.end = end;
- }
-
- @disable this();
-
- @property ByCodeUnit save()
- {
- return this;
- }
-
- @property bool empty() const
- {
- return this.begin == this.end;
- }
-
- @property size_t length() const
- {
- return this.end - this.begin;
- }
-
- alias opDollar = length;
-
- @property ref inout(E) front() inout
- in
- {
- assert(!empty);
- }
- body
- {
- return *this.begin;
- }
-
- @property ref inout(E) back() inout @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- return *(this.end - 1);
- }
-
- void popFront() @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- ++this.begin;
- }
-
- void popBack() @trusted
- in
- {
- assert(!empty);
- }
- body
- {
- --this.end;
- }
-
- ref inout(E) opIndex(const size_t i) inout @trusted
- in
- {
- assert(i < length);
- }
- body
- {
- return *(this.begin + i);
- }
-
- ByCodeUnit opIndex()
- {
- return typeof(return)(*this.container, this.begin, this.end);
- }
-
- ByCodeUnit!(const E) opIndex() const
- {
- return typeof(return)(*this.container, this.begin, this.end);
- }
-
- ByCodeUnit opSlice(const size_t i, const size_t j) @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(*this.container, this.begin + i, this.begin + j);
- }
-
- ByCodeUnit!(const E) opSlice(const size_t i, const size_t j) const @trusted
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(*this.container, this.begin + i, this.begin + j);
- }
-
- inout(E[]) get() inout @trusted
- {
- return this.begin[0 .. length];
- }
-}
-
-/// UTF-8 string.
-alias String = Slice!char;
-
-/**
- * UTF-8 string.
- *
- * Params:
- * T = $(D_KEYWORD char).
- */
-struct Slice(T)
- if (is(T == char))
-{
- private size_t length_;
- private char* data;
- private size_t capacity_;
-
- pure nothrow @safe @nogc invariant
- {
- assert(this.length_ <= this.capacity_);
- }
-
- /**
- * Constructs the string from a stringish range.
- *
- * Params:
- * R = String type.
- * str = Initial string.
- * allocator = Allocator.
- *
- * Throws: $(D_PSYMBOL UTFException).
- *
- * Precondition: $(D_INLINECODE allocator is null).
- */
- this(R)(const R str, shared Allocator allocator = defaultAllocator)
- if (!isInfinite!R
- && isInputRange!R
- && isSomeChar!(ElementEncodingType!R))
- {
- this(allocator);
- insertBack(str);
- }
-
- ///
- @safe @nogc unittest
- {
- auto s = String("\u10437"w);
- assert("\u10437" == s.get());
- }
-
- ///
- @safe @nogc unittest
- {
- auto s = String("Отказаться от вина - в этом страшная вина."d);
- assert("Отказаться от вина - в этом страшная вина." == s.get());
- }
-
- /**
- * Initializes this string from another one.
- *
- * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
- * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
- * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
- * storage, otherwise, the storage will be allocated with
- * $(D_PARAM allocator). $(D_PARAM init) will be destroyed at the end.
- *
- * If $(D_PARAM init) is passed by reference, it will be copied.
- *
- * Params:
- * init = Source string.
- * allocator = Allocator.
- *
- * Precondition: $(D_INLINECODE allocator is null).
- */
- this(Slice!char init, shared Allocator allocator = defaultAllocator)
- nothrow @trusted @nogc
- {
- this(allocator);
- if (allocator !is init.allocator)
- {
- // Just steal all references and the allocator.
- this.data = init.data;
- this.length_ = init.length_;
- this.capacity_ = init.capacity_;
-
- // Reset the source string, so it can't destroy the moved storage.
- init.length_ = init.capacity_ = 0;
- init.data = null;
- }
- else
- {
- reserve(init.length);
- init.data[0 .. init.length].copy(this.data[0 .. init.length]);
- this.length_ = init.length;
- }
- }
-
- /// Ditto.
- this(ref const Slice!char init, shared Allocator allocator = defaultAllocator)
- nothrow @trusted @nogc
- {
- this(allocator);
- reserve(init.length);
- init.data[0 .. init.length].copy(this.data[0 .. init.length]);
- this.length_ = init.length;
- }
-
- /// Ditto.
- this(shared Allocator allocator) pure nothrow @safe @nogc
- in
- {
- assert(allocator !is null);
- }
- body
- {
- this.allocator_ = allocator;
- }
-
- /**
- * Fills the string with $(D_PARAM n) consecutive copies of character $(D_PARAM chr).
- *
- * Params:
- * C = Type of the character to fill the string with.
- * n = Number of characters to copy.
- * chr = Character to fill the string with.
- */
- this(C)(const size_t n, const C chr,
- shared Allocator allocator = defaultAllocator) @trusted
- if (isSomeChar!C)
- {
- this(allocator);
- if (n == 0)
- {
- return;
- }
- insertBack(chr);
-
- // insertBack should validate the character, so we can just copy it
- // n - 1 times.
- auto remaining = length * n;
-
- reserve(remaining);
-
- // Use a quick copy.
- for (auto i = this.length_ * 2; i <= remaining; i *= 2)
- {
- this.data[0 .. this.length_].copy(this.data[this.length_ .. i]);
- this.length_ = i;
- }
- remaining -= length;
- copy(this.data[this.length_ - remaining .. this.length_],
- this.data[this.length_ .. this.length_ + remaining]);
- this.length_ += remaining;
- }
-
- private unittest
- {
- {
- auto s = String(1, 'О');
- assert(s.length == 2);
- }
- {
- auto s = String(3, 'О');
- assert(s.length == 6);
- }
- {
- auto s = String(8, 'О');
- assert(s.length == 16);
- }
- }
-
- /**
- * Destroys the string.
- */
- ~this() nothrow @trusted @nogc
- {
- allocator.deallocate(this.data[0 .. this.capacity_]);
- }
-
- private void write4Bytes(ref const dchar src)
- pure nothrow @trusted @nogc
- in
- {
- assert(capacity - length >= 4);
- assert(src - 0x10000 < 0x100000);
- }
- body
- {
- auto dst = this.data + length;
-
- *dst++ = 0xf0 | (src >> 18);
- *dst++ = 0x80 | ((src >> 12) & 0x3f);
- *dst++ = 0x80 | ((src >> 6) & 0x3f);
- *dst = 0x80 | (src & 0x3f);
-
- this.length_ += 4;
- }
-
- private size_t insertWideChar(C)(auto ref const C chr) @trusted
- if (is(C == wchar) || is(C == dchar))
- in
- {
- assert(capacity - length >= C.sizeof);
- }
- body
- {
- auto dst = this.data + length;
- if (chr < 0x80)
- {
- *dst = chr & 0x7f;
- this.length_ += 1;
- return 1;
- }
- else if (chr < 0x800)
- {
- *dst++ = 0xc0 | (chr >> 6) & 0xff;
- *dst = 0x80 | (chr & 0x3f);
- this.length_ += 2;
- return 2;
- }
- else if (chr < 0xd800 || chr - 0xe000 < 0x2000)
- {
- *dst++ = 0xe0 | (chr >> 12) & 0xff;
- *dst++ = 0x80 | ((chr >> 6) & 0x3f);
- *dst = 0x80 | (chr & 0x3f);
- this.length_ += 3;
- return 3;
- }
- return 0;
- }
-
- /**
- * Inserts a single character at the end of the string.
- *
- * Params:
- * chr = The character should be inserted.
- *
- * Returns: The number of bytes inserted.
- *
- * Throws: $(D_PSYMBOL UTFException).
- */
- size_t insertBack(const char chr) @trusted @nogc
- {
- if ((chr & 0x80) != 0)
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
- }
- reserve(length + 1);
-
- *(data + length) = chr;
- ++this.length_;
-
- return 1;
- }
-
- /// Ditto.
- size_t insertBack(const wchar chr) @trusted @nogc
- {
- reserve(length + wchar.sizeof);
-
- auto ret = insertWideChar(chr);
- if (ret == 0)
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
- }
- return ret;
- }
-
- /// Ditto.
- size_t insertBack(const dchar chr) @trusted @nogc
- {
- reserve(length + dchar.sizeof);
-
- auto ret = insertWideChar(chr);
- if (ret > 0)
- {
- return ret;
- }
- else if (chr - 0x10000 < 0x100000)
- {
- write4Bytes(chr);
- return 4;
- }
- else
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-32 sequeunce");
- }
- }
-
- /**
- * Inserts a stringish range at the end of the string.
- *
- * Params:
- * R = Type of the inserted string.
- * str = String should be inserted.
- *
- * Returns: The number of bytes inserted.
- *
- * Throws: $(D_PSYMBOL UTFException).
- */
- size_t insertBack(R)(R str) @trusted
- if (!isInfinite!R
- && isInputRange!R
- && is(Unqual!(ElementEncodingType!R) == char))
- {
- size_t size;
- static if (hasLength!R || isNarrowString!R)
- {
- size = str.length + length;
- reserve(size);
- }
-
- static if (isNarrowString!R)
- {
- str.copy(this.data[length .. size]);
- this.length_ = size;
- return str.length;
- }
- else
- {
- size_t insertedLength;
- while (!str.empty)
- {
- ubyte expectedLength;
- if ((str.front & 0x80) == 0x00)
- {
- expectedLength = 1;
- }
- else if ((str.front & 0xe0) == 0xc0)
- {
- expectedLength = 2;
- }
- else if ((str.front & 0xf0) == 0xe0)
- {
- expectedLength = 3;
- }
- else if ((str.front & 0xf8) == 0xf0)
- {
- expectedLength = 4;
- }
- else
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
- }
- size = length + expectedLength;
- reserve(size);
-
- for (; expectedLength > 0; --expectedLength)
- {
- if (str.empty)
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
- }
- *(data + length) = str.front;
- str.popFront();
- }
- insertedLength += expectedLength;
- this.length_ = size;
- }
- return insertedLength;
- }
- }
-
- /// Ditto.
- size_t insertBack(R)(R str) @trusted
- if (!isInfinite!R
- && isInputRange!R
- && is(Unqual!(ElementEncodingType!R) == wchar))
- {
- static if (hasLength!R || isNarrowString!R)
- {
- reserve(length + str.length * wchar.sizeof);
- }
-
- static if (isNarrowString!R)
- {
- const(wchar)[] range = str;
- }
- else
- {
- alias range = str;
- }
-
- auto oldLength = length;
-
- while (!range.empty)
- {
- reserve(length + 4);
-
- auto ret = insertWideChar(range.front);
- if (ret > 0)
- {
- range.popFront();
- }
- else if (range.front - 0xd800 < 2048)
- { // Surrogate pair.
- static if (isNarrowString!R)
- {
- if (range.length < 2 || range[1] - 0xdc00 >= 0x400)
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
- }
- dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
-
- range.popFront(2);
- }
- else
- {
- dchar d = range.front - 0xd800;
- range.popFront();
-
- if (range.empty || range.front - 0xdc00 >= 0x400)
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
- }
- d |= (range.front - 0xdc00) >> 10;
-
- range.popFront();
- }
- write4Bytes(d);
- }
- else
- {
- throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
- }
- }
- return this.length_ - oldLength;
- }
-
- /// Ditto.
- size_t insertBack(R)(R str) @trusted
- if (!isInfinite!R
- && isInputRange!R
- && is(Unqual!(ElementEncodingType!R) == dchar))
- {
- static if (hasLength!R || isSomeString!R)
- {
- reserve(length + str.length * 4);
- }
-
- size_t insertedLength;
- foreach (const dchar c; str)
- {
- insertedLength += insertBack(c);
- }
- return insertedLength;
- }
-
- /// Ditto.
- alias insert = insertBack;
-
- /**
- * Reserves $(D_PARAM size) bytes for the string.
- *
- * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the
- * function call does not cause a reallocation and the string capacity is not
- * affected.
- *
- * Params:
- * size = Desired size in bytes.
- */
- void reserve(const size_t size) nothrow @trusted @nogc
- {
- if (this.capacity_ >= size)
- {
- return;
- }
-
- this.data = allocator.resize(this.data[0 .. this.capacity_], size).ptr;
- this.capacity_ = size;
- }
-
- ///
- @nogc @safe unittest
- {
- String s;
- assert(s.capacity == 0);
-
- s.reserve(3);
- assert(s.capacity == 3);
-
- s.reserve(3);
- assert(s.capacity == 3);
-
- s.reserve(1);
- assert(s.capacity == 3);
- }
-
- /**
- * Requests the string to reduce its capacity to fit the $(D_PARAM size).
- *
- * The request is non-binding. The string won't become smaller than the
- * string byte length.
- *
- * Params:
- * size = Desired size.
- */
- void shrink(const size_t size) nothrow @trusted @nogc
- {
- if (this.capacity_ <= size)
- {
- return;
- }
-
- const n = max(this.length_, size);
- void[] buf = this.data[0 .. this.capacity_];
- if (allocator.reallocate(buf, n))
- {
- this.capacity_ = n;
- this.data = cast(char*) buf;
- }
- }
-
- ///
- @nogc @safe unittest
- {
- auto s = String("Die Alten lasen laut.");
- assert(s.capacity == 21);
-
- s.reserve(30);
- s.shrink(25);
- assert(s.capacity == 25);
-
- s.shrink(18);
- assert(s.capacity == 21);
- }
-
- /**
- * Returns: String capacity in bytes.
- */
- @property size_t capacity() const pure nothrow @safe @nogc
- {
- return this.capacity_;
- }
-
- ///
- unittest
- {
- auto s = String("In allem Schreiben ist Schamlosigkeit.");
- assert(s.capacity == 38);
- }
-
- /**
- * Returns an array used internally by the string.
- * The length of the returned array may be smaller than the size of the
- * reserved memory for the string.
- *
- * Returns: The array representing the string.
- */
- inout(char[]) get() inout pure nothrow @trusted @nogc
- {
- return this.data[0 .. this.length_];
- }
-
- /**
- * Returns: The number of code units that are required to encode the string.
- */
- @property size_t length() const pure nothrow @safe @nogc
- {
- return this.length_;
- }
-
- ///
- alias opDollar = length;
-
- ///
- unittest
- {
- auto s = String("Piscis primuin a capite foetat.");
- assert(s.length == 31);
- assert(s[$ - 1] == '.');
- }
-
- /**
- * Params:
- * pos = Position.
- *
- * Returns: Byte at $(D_PARAM pos).
- *
- * Precondition: $(D_INLINECODE length > pos).
- */
- ref inout(char) opIndex(const size_t pos) inout pure nothrow @trusted @nogc
- in
- {
- assert(length > pos);
- }
- body
- {
- return *(this.data + pos);
- }
-
- ///
- unittest
- {
- auto s = String("Alea iacta est.");
- assert(s[0] == 'A');
- assert(s[4] == ' ');
- }
-
- /**
- * Returns: Random access range that iterates over the string by bytes, in
- * forward order.
- */
- ByCodeUnit!char opIndex() pure nothrow @trusted @nogc
- {
- return typeof(return)(this, this.data, this.data + length);
- }
-
- /// Ditto.
- ByCodeUnit!(const char) opIndex() const pure nothrow @trusted @nogc
- {
- return typeof(return)(this, this.data, this.data + length);
- }
-
- ///
- unittest
- {
- auto s = String("Plutarchus");
- auto r = s[];
- assert(r.front == 'P');
- assert(r.back == 's');
-
- r.popFront();
- assert(r.front == 'l');
- assert(r.back == 's');
-
- r.popBack();
- assert(r.front == 'l');
- assert(r.back == 'u');
-
- assert(r.length == 8);
- }
-
- /**
- * Returns: $(D_KEYWORD true) if the vector is empty.
- */
- @property bool empty() const pure nothrow @safe @nogc
- {
- return length == 0;
- }
-
- /**
- * Params:
- * i = Slice start.
- * j = Slice end.
- *
- * Returns: A range that iterates over the string by bytes from
- * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
- *
- * Precondition: $(D_INLINECODE i <= j && j <= length).
- */
- ByCodeUnit!char opSlice(const size_t i, const size_t j)
- pure nothrow @trusted @nogc
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(this, this.data + i, this.data + j);
- }
-
- /// Ditto.
- ByCodeUnit!(const char) opSlice(const size_t i, const size_t j)
- const pure nothrow @trusted @nogc
- in
- {
- assert(i <= j);
- assert(j <= length);
- }
- body
- {
- return typeof(return)(this, this.data + i, this.data + j);
- }
-
- ///
- unittest
- {
- auto s = String("Vladimir Soloviev");
- auto r = s[9 .. $];
-
- assert(r.front == 'S');
- assert(r.back == 'v');
-
- r.popFront();
- r.popBack();
- assert(r.front == 'o');
- assert(r.back == 'e');
-
- r.popFront();
- r.popBack();
- assert(r.front == 'l');
- assert(r.back == 'i');
-
- r.popFront();
- r.popBack();
- assert(r.front == 'o');
- assert(r.back == 'v');
-
- r.popFront();
- r.popBack();
- assert(r.empty);
- }
-
- mixin DefaultAllocator;
-}
diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d
new file mode 100644
index 0000000..4737fab
--- /dev/null
+++ b/source/tanya/container/string.d
@@ -0,0 +1,905 @@
+/* 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/. */
+
+/**
+ * UTF-8 string.
+ *
+ * Copyright: Eugene Wissner 2017.
+ * 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)
+ */
+module tanya.container.string;
+
+import core.exception;
+import std.algorithm.comparison;
+import std.algorithm.mutation;
+import std.range;
+import std.traits;
+import tanya.memory;
+
+private ref const(wchar) front(const wchar[] str)
+pure nothrow @safe @nogc
+in
+{
+ assert(str.length > 0);
+}
+body
+{
+ return str[0];
+}
+
+private void popFront(ref const(wchar)[] str, const size_t s = 1)
+pure nothrow @safe @nogc
+in
+{
+ assert(str.length >= s);
+}
+body
+{
+ str = str[s .. $];
+}
+
+/**
+ * Thrown on encoding errors.
+ */
+class UTFException : Exception
+{
+ /**
+ * Params:
+ * msg = The message for the exception.
+ * file = The file where the exception occurred.
+ * line = The line number where the exception occurred.
+ * next = The previous exception in the chain of exceptions, if any.
+ */
+ this(string msg,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null) @nogc @safe pure nothrow
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/**
+ * Iterates $(D_PSYMBOL String) by UTF-8 code unit.
+ *
+ * Params:
+ * E = Element type ($(D_KEYWORD char) or $(D_INLINECODE const(char))).
+ */
+struct ByCodeUnit(E)
+ if (is(Unqual!E == char))
+{
+ private E* begin, end;
+ private alias ContainerType = CopyConstness!(E, String);
+ private ContainerType* container;
+
+ invariant
+ {
+ assert(this.begin <= this.end);
+ assert(this.container !is null);
+ assert(this.begin >= this.container.data);
+ assert(this.end <= this.container.data + this.container.length);
+ }
+
+ private this(ref ContainerType container, E* begin, E* end) @trusted
+ in
+ {
+ assert(begin <= end);
+ assert(begin >= container.data);
+ assert(end <= container.data + container.length);
+ }
+ body
+ {
+ this.container = &container;
+ this.begin = begin;
+ this.end = end;
+ }
+
+ @disable this();
+
+ @property ByCodeUnit save()
+ {
+ return this;
+ }
+
+ @property bool empty() const
+ {
+ return this.begin == this.end;
+ }
+
+ @property size_t length() const
+ {
+ return this.end - this.begin;
+ }
+
+ alias opDollar = length;
+
+ @property ref inout(E) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *this.begin;
+ }
+
+ @property ref inout(E) back() inout @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *(this.end - 1);
+ }
+
+ void popFront() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ ++this.begin;
+ }
+
+ void popBack() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ --this.end;
+ }
+
+ ref inout(E) opIndex(const size_t i) inout @trusted
+ in
+ {
+ assert(i < length);
+ }
+ body
+ {
+ return *(this.begin + i);
+ }
+
+ ByCodeUnit opIndex()
+ {
+ return typeof(return)(*this.container, this.begin, this.end);
+ }
+
+ ByCodeUnit!(const E) opIndex() const
+ {
+ return typeof(return)(*this.container, this.begin, this.end);
+ }
+
+ ByCodeUnit opSlice(const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(*this.container, this.begin + i, this.begin + j);
+ }
+
+ ByCodeUnit!(const E) opSlice(const size_t i, const size_t j) const @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(*this.container, this.begin + i, this.begin + j);
+ }
+
+ inout(E[]) get() inout @trusted
+ {
+ return this.begin[0 .. length];
+ }
+}
+
+/**
+ * UTF-8 string.
+ */
+struct String
+{
+ private size_t length_;
+ private char* data;
+ private size_t capacity_;
+
+ pure nothrow @safe @nogc invariant
+ {
+ assert(this.length_ <= this.capacity_);
+ }
+
+ /**
+ * Constructs the string from a stringish range.
+ *
+ * Params:
+ * R = String type.
+ * str = Initial string.
+ * allocator = Allocator.
+ *
+ * Throws: $(D_PSYMBOL UTFException).
+ *
+ * Precondition: $(D_INLINECODE allocator is null).
+ */
+ this(R)(const R str, shared Allocator allocator = defaultAllocator)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isSomeChar!(ElementEncodingType!R))
+ {
+ this(allocator);
+ insertBack(str);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto s = String("\u10437"w);
+ assert("\u10437" == s.get());
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto s = String("Отказаться от вина - в этом страшная вина."d);
+ assert("Отказаться от вина - в этом страшная вина." == s.get());
+ }
+
+ /**
+ * Initializes this string from another one.
+ *
+ * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
+ * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
+ * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
+ * storage, otherwise, the storage will be allocated with
+ * $(D_PARAM allocator). $(D_PARAM init) will be destroyed at the end.
+ *
+ * If $(D_PARAM init) is passed by reference, it will be copied.
+ *
+ * Params:
+ * init = Source string.
+ * allocator = Allocator.
+ *
+ * Precondition: $(D_INLINECODE allocator is null).
+ */
+ this(String init, shared Allocator allocator = defaultAllocator)
+ nothrow @trusted @nogc
+ {
+ this(allocator);
+ if (allocator !is init.allocator)
+ {
+ // Just steal all references and the allocator.
+ this.data = init.data;
+ this.length_ = init.length_;
+ this.capacity_ = init.capacity_;
+
+ // Reset the source string, so it can't destroy the moved storage.
+ init.length_ = init.capacity_ = 0;
+ init.data = null;
+ }
+ else
+ {
+ reserve(init.length);
+ init.data[0 .. init.length].copy(this.data[0 .. init.length]);
+ this.length_ = init.length;
+ }
+ }
+
+ /// Ditto.
+ this(ref const String init, shared Allocator allocator = defaultAllocator)
+ nothrow @trusted @nogc
+ {
+ this(allocator);
+ reserve(init.length);
+ init.data[0 .. init.length].copy(this.data[0 .. init.length]);
+ this.length_ = init.length;
+ }
+
+ /// Ditto.
+ this(shared Allocator allocator) pure nothrow @safe @nogc
+ in
+ {
+ assert(allocator !is null);
+ }
+ body
+ {
+ this.allocator_ = allocator;
+ }
+
+ /**
+ * Fills the string with $(D_PARAM n) consecutive copies of character $(D_PARAM chr).
+ *
+ * Params:
+ * C = Type of the character to fill the string with.
+ * n = Number of characters to copy.
+ * chr = Character to fill the string with.
+ */
+ this(C)(const size_t n, const C chr,
+ shared Allocator allocator = defaultAllocator) @trusted
+ if (isSomeChar!C)
+ {
+ this(allocator);
+ if (n == 0)
+ {
+ return;
+ }
+ insertBack(chr);
+
+ // insertBack should validate the character, so we can just copy it
+ // n - 1 times.
+ auto remaining = length * n;
+
+ reserve(remaining);
+
+ // Use a quick copy.
+ for (auto i = this.length_ * 2; i <= remaining; i *= 2)
+ {
+ this.data[0 .. this.length_].copy(this.data[this.length_ .. i]);
+ this.length_ = i;
+ }
+ remaining -= length;
+ copy(this.data[this.length_ - remaining .. this.length_],
+ this.data[this.length_ .. this.length_ + remaining]);
+ this.length_ += remaining;
+ }
+
+ private unittest
+ {
+ {
+ auto s = String(1, 'О');
+ assert(s.length == 2);
+ }
+ {
+ auto s = String(3, 'О');
+ assert(s.length == 6);
+ }
+ {
+ auto s = String(8, 'О');
+ assert(s.length == 16);
+ }
+ }
+
+ /**
+ * Destroys the string.
+ */
+ ~this() nothrow @trusted @nogc
+ {
+ allocator.deallocate(this.data[0 .. this.capacity_]);
+ }
+
+ private void write4Bytes(ref const dchar src)
+ pure nothrow @trusted @nogc
+ in
+ {
+ assert(capacity - length >= 4);
+ assert(src - 0x10000 < 0x100000);
+ }
+ body
+ {
+ auto dst = this.data + length;
+
+ *dst++ = 0xf0 | (src >> 18);
+ *dst++ = 0x80 | ((src >> 12) & 0x3f);
+ *dst++ = 0x80 | ((src >> 6) & 0x3f);
+ *dst = 0x80 | (src & 0x3f);
+
+ this.length_ += 4;
+ }
+
+ private size_t insertWideChar(C)(auto ref const C chr) @trusted
+ if (is(C == wchar) || is(C == dchar))
+ in
+ {
+ assert(capacity - length >= C.sizeof);
+ }
+ body
+ {
+ auto dst = this.data + length;
+ if (chr < 0x80)
+ {
+ *dst = chr & 0x7f;
+ this.length_ += 1;
+ return 1;
+ }
+ else if (chr < 0x800)
+ {
+ *dst++ = 0xc0 | (chr >> 6) & 0xff;
+ *dst = 0x80 | (chr & 0x3f);
+ this.length_ += 2;
+ return 2;
+ }
+ else if (chr < 0xd800 || chr - 0xe000 < 0x2000)
+ {
+ *dst++ = 0xe0 | (chr >> 12) & 0xff;
+ *dst++ = 0x80 | ((chr >> 6) & 0x3f);
+ *dst = 0x80 | (chr & 0x3f);
+ this.length_ += 3;
+ return 3;
+ }
+ return 0;
+ }
+
+ /**
+ * Inserts a single character at the end of the string.
+ *
+ * Params:
+ * chr = The character should be inserted.
+ *
+ * Returns: The number of bytes inserted.
+ *
+ * Throws: $(D_PSYMBOL UTFException).
+ */
+ size_t insertBack(const char chr) @trusted @nogc
+ {
+ if ((chr & 0x80) != 0)
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-8 character");
+ }
+ reserve(length + 1);
+
+ *(data + length) = chr;
+ ++this.length_;
+
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBack(const wchar chr) @trusted @nogc
+ {
+ reserve(length + wchar.sizeof);
+
+ auto ret = insertWideChar(chr);
+ if (ret == 0)
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
+ }
+ return ret;
+ }
+
+ /// Ditto.
+ size_t insertBack(const dchar chr) @trusted @nogc
+ {
+ reserve(length + dchar.sizeof);
+
+ auto ret = insertWideChar(chr);
+ if (ret > 0)
+ {
+ return ret;
+ }
+ else if (chr - 0x10000 < 0x100000)
+ {
+ write4Bytes(chr);
+ return 4;
+ }
+ else
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-32 sequeunce");
+ }
+ }
+
+ /**
+ * Inserts a stringish range at the end of the string.
+ *
+ * Params:
+ * R = Type of the inserted string.
+ * str = String should be inserted.
+ *
+ * Returns: The number of bytes inserted.
+ *
+ * Throws: $(D_PSYMBOL UTFException).
+ */
+ size_t insertBack(R)(R str) @trusted
+ if (!isInfinite!R
+ && isInputRange!R
+ && is(Unqual!(ElementEncodingType!R) == char))
+ {
+ size_t size;
+ static if (hasLength!R || isNarrowString!R)
+ {
+ size = str.length + length;
+ reserve(size);
+ }
+
+ static if (isNarrowString!R)
+ {
+ str.copy(this.data[length .. size]);
+ this.length_ = size;
+ return str.length;
+ }
+ else
+ {
+ size_t insertedLength;
+ while (!str.empty)
+ {
+ ubyte expectedLength;
+ if ((str.front & 0x80) == 0x00)
+ {
+ expectedLength = 1;
+ }
+ else if ((str.front & 0xe0) == 0xc0)
+ {
+ expectedLength = 2;
+ }
+ else if ((str.front & 0xf0) == 0xe0)
+ {
+ expectedLength = 3;
+ }
+ else if ((str.front & 0xf8) == 0xf0)
+ {
+ expectedLength = 4;
+ }
+ else
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
+ }
+ size = length + expectedLength;
+ reserve(size);
+
+ for (; expectedLength > 0; --expectedLength)
+ {
+ if (str.empty)
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-8 sequeunce");
+ }
+ *(data + length) = str.front;
+ str.popFront();
+ }
+ insertedLength += expectedLength;
+ this.length_ = size;
+ }
+ return insertedLength;
+ }
+ }
+
+ /// Ditto.
+ size_t insertBack(R)(R str) @trusted
+ if (!isInfinite!R
+ && isInputRange!R
+ && is(Unqual!(ElementEncodingType!R) == wchar))
+ {
+ static if (hasLength!R || isNarrowString!R)
+ {
+ reserve(length + str.length * wchar.sizeof);
+ }
+
+ static if (isNarrowString!R)
+ {
+ const(wchar)[] range = str;
+ }
+ else
+ {
+ alias range = str;
+ }
+
+ auto oldLength = length;
+
+ while (!range.empty)
+ {
+ reserve(length + 4);
+
+ auto ret = insertWideChar(range.front);
+ if (ret > 0)
+ {
+ range.popFront();
+ }
+ else if (range.front - 0xd800 < 2048)
+ { // Surrogate pair.
+ static if (isNarrowString!R)
+ {
+ if (range.length < 2 || range[1] - 0xdc00 >= 0x400)
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
+ }
+ dchar d = (range[0] - 0xd800) | ((range[1] - 0xdc00) >> 10);
+
+ range.popFront(2);
+ }
+ else
+ {
+ dchar d = range.front - 0xd800;
+ range.popFront();
+
+ if (range.empty || range.front - 0xdc00 >= 0x400)
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
+ }
+ d |= (range.front - 0xdc00) >> 10;
+
+ range.popFront();
+ }
+ write4Bytes(d);
+ }
+ else
+ {
+ throw defaultAllocator.make!UTFException("Invalid UTF-16 sequeunce");
+ }
+ }
+ return this.length_ - oldLength;
+ }
+
+ /// Ditto.
+ size_t insertBack(R)(R str) @trusted
+ if (!isInfinite!R
+ && isInputRange!R
+ && is(Unqual!(ElementEncodingType!R) == dchar))
+ {
+ static if (hasLength!R || isSomeString!R)
+ {
+ reserve(length + str.length * 4);
+ }
+
+ size_t insertedLength;
+ foreach (const dchar c; str)
+ {
+ insertedLength += insertBack(c);
+ }
+ return insertedLength;
+ }
+
+ /// Ditto.
+ alias insert = insertBack;
+
+ /**
+ * Reserves $(D_PARAM size) bytes for the string.
+ *
+ * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the
+ * function call does not cause a reallocation and the string capacity is not
+ * affected.
+ *
+ * Params:
+ * size = Desired size in bytes.
+ */
+ void reserve(const size_t size) nothrow @trusted @nogc
+ {
+ if (this.capacity_ >= size)
+ {
+ return;
+ }
+
+ this.data = allocator.resize(this.data[0 .. this.capacity_], size).ptr;
+ this.capacity_ = size;
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ String s;
+ assert(s.capacity == 0);
+
+ s.reserve(3);
+ assert(s.capacity == 3);
+
+ s.reserve(3);
+ assert(s.capacity == 3);
+
+ s.reserve(1);
+ assert(s.capacity == 3);
+ }
+
+ /**
+ * Requests the string to reduce its capacity to fit the $(D_PARAM size).
+ *
+ * The request is non-binding. The string won't become smaller than the
+ * string byte length.
+ *
+ * Params:
+ * size = Desired size.
+ */
+ void shrink(const size_t size) nothrow @trusted @nogc
+ {
+ if (this.capacity_ <= size)
+ {
+ return;
+ }
+
+ const n = max(this.length_, size);
+ void[] buf = this.data[0 .. this.capacity_];
+ if (allocator.reallocate(buf, n))
+ {
+ this.capacity_ = n;
+ this.data = cast(char*) buf;
+ }
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ auto s = String("Die Alten lasen laut.");
+ assert(s.capacity == 21);
+
+ s.reserve(30);
+ s.shrink(25);
+ assert(s.capacity == 25);
+
+ s.shrink(18);
+ assert(s.capacity == 21);
+ }
+
+ /**
+ * Returns: String capacity in bytes.
+ */
+ @property size_t capacity() const pure nothrow @safe @nogc
+ {
+ return this.capacity_;
+ }
+
+ ///
+ unittest
+ {
+ auto s = String("In allem Schreiben ist Schamlosigkeit.");
+ assert(s.capacity == 38);
+ }
+
+ /**
+ * Returns an array used internally by the string.
+ * The length of the returned array may be smaller than the size of the
+ * reserved memory for the string.
+ *
+ * Returns: The array representing the string.
+ */
+ inout(char[]) get() inout pure nothrow @trusted @nogc
+ {
+ return this.data[0 .. this.length_];
+ }
+
+ /**
+ * Returns: The number of code units that are required to encode the string.
+ */
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return this.length_;
+ }
+
+ ///
+ alias opDollar = length;
+
+ ///
+ unittest
+ {
+ auto s = String("Piscis primuin a capite foetat.");
+ assert(s.length == 31);
+ assert(s[$ - 1] == '.');
+ }
+
+ /**
+ * Params:
+ * pos = Position.
+ *
+ * Returns: Byte at $(D_PARAM pos).
+ *
+ * Precondition: $(D_INLINECODE length > pos).
+ */
+ ref inout(char) opIndex(const size_t pos) inout pure nothrow @trusted @nogc
+ in
+ {
+ assert(length > pos);
+ }
+ body
+ {
+ return *(this.data + pos);
+ }
+
+ ///
+ unittest
+ {
+ auto s = String("Alea iacta est.");
+ assert(s[0] == 'A');
+ assert(s[4] == ' ');
+ }
+
+ /**
+ * Returns: Random access range that iterates over the string by bytes, in
+ * forward order.
+ */
+ ByCodeUnit!char opIndex() pure nothrow @trusted @nogc
+ {
+ return typeof(return)(this, this.data, this.data + length);
+ }
+
+ /// Ditto.
+ ByCodeUnit!(const char) opIndex() const pure nothrow @trusted @nogc
+ {
+ return typeof(return)(this, this.data, this.data + length);
+ }
+
+ ///
+ unittest
+ {
+ auto s = String("Plutarchus");
+ auto r = s[];
+ assert(r.front == 'P');
+ assert(r.back == 's');
+
+ r.popFront();
+ assert(r.front == 'l');
+ assert(r.back == 's');
+
+ r.popBack();
+ assert(r.front == 'l');
+ assert(r.back == 'u');
+
+ assert(r.length == 8);
+ }
+
+ /**
+ * Returns: $(D_KEYWORD true) if the vector is empty.
+ */
+ @property bool empty() const pure nothrow @safe @nogc
+ {
+ return length == 0;
+ }
+
+ /**
+ * Params:
+ * i = Slice start.
+ * j = Slice end.
+ *
+ * Returns: A range that iterates over the string by bytes from
+ * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
+ *
+ * Precondition: $(D_INLINECODE i <= j && j <= length).
+ */
+ ByCodeUnit!char opSlice(const size_t i, const size_t j)
+ pure nothrow @trusted @nogc
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(this, this.data + i, this.data + j);
+ }
+
+ /// Ditto.
+ ByCodeUnit!(const char) opSlice(const size_t i, const size_t j)
+ const pure nothrow @trusted @nogc
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(this, this.data + i, this.data + j);
+ }
+
+ ///
+ unittest
+ {
+ auto s = String("Vladimir Soloviev");
+ auto r = s[9 .. $];
+
+ assert(r.front == 'S');
+ assert(r.back == 'v');
+
+ r.popFront();
+ r.popBack();
+ assert(r.front == 'o');
+ assert(r.back == 'e');
+
+ r.popFront();
+ r.popBack();
+ assert(r.front == 'l');
+ assert(r.back == 'i');
+
+ r.popFront();
+ r.popBack();
+ assert(r.front == 'o');
+ assert(r.back == 'v');
+
+ r.popFront();
+ r.popBack();
+ assert(r.empty);
+ }
+
+ mixin DefaultAllocator;
+}
diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d
index 8e4fa01..1e9360a 100644
--- a/source/tanya/container/vector.d
+++ b/source/tanya/container/vector.d
@@ -12,4 +12,1644 @@
*/
module tanya.container.vector;
-public import tanya.container.slice;
+import core.checkedint;
+import core.exception;
+import std.algorithm.comparison;
+import std.algorithm.mutation;
+import std.conv;
+import std.range.primitives;
+import std.meta;
+import std.traits;
+import tanya.memory;
+
+/**
+ * Random-access range for the $(D_PSYMBOL Vector).
+ *
+ * Params:
+ * E = Element type.
+ */
+struct Range(E)
+{
+ private E* begin, end;
+ private alias ContainerType = CopyConstness!(E, Vector!(Unqual!E));
+ private ContainerType* vector;
+
+ invariant
+ {
+ assert(this.begin <= this.end);
+ assert(this.vector !is null);
+ assert(this.begin >= this.vector.data);
+ assert(this.end <= this.vector.data + this.vector.length);
+ }
+
+ private this(ref ContainerType vector, E* begin, E* end) @trusted
+ in
+ {
+ assert(begin <= end);
+ assert(begin >= vector.data);
+ assert(end <= vector.data + vector.length);
+ }
+ body
+ {
+ this.vector = &vector;
+ this.begin = begin;
+ this.end = end;
+ }
+
+ @disable this();
+
+ @property Range save()
+ {
+ return this;
+ }
+
+ @property bool empty() const
+ {
+ return this.begin == this.end;
+ }
+
+ @property size_t length() const
+ {
+ return this.end - this.begin;
+ }
+
+ alias opDollar = length;
+
+ @property ref inout(E) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *this.begin;
+ }
+
+ @property ref inout(E) back() inout @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *(this.end - 1);
+ }
+
+ void popFront() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ ++this.begin;
+ }
+
+ void popBack() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ --this.end;
+ }
+
+ ref inout(E) opIndex(const size_t i) inout @trusted
+ in
+ {
+ assert(i < length);
+ }
+ body
+ {
+ return *(this.begin + i);
+ }
+
+ Range opIndex()
+ {
+ return typeof(return)(*this.vector, this.begin, this.end);
+ }
+
+ Range!(const E) opIndex() const
+ {
+ return typeof(return)(*this.vector, this.begin, this.end);
+ }
+
+ Range opSlice(const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(*this.vector, this.begin + i, this.begin + j);
+ }
+
+ Range!(const E) opSlice(const size_t i, const size_t j) const @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(*this.vector, this.begin + i, this.begin + j);
+ }
+
+ inout(E[]) get() inout @trusted
+ {
+ return this.begin[0 .. length];
+ }
+}
+
+/**
+ * One dimensional array.
+ *
+ * Params:
+ * T = Content type.
+ */
+struct Vector(T)
+{
+ private size_t length_;
+ private T* data;
+ private size_t capacity_;
+
+ invariant
+ {
+ assert(this.length_ <= this.capacity_);
+ assert(this.capacity_ == 0 || this.data !is null);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL Vector) with the elements from a static array.
+ *
+ * Params:
+ * R = Static array size.
+ * init = Values to initialize the vector with.
+ * allocator = Allocator.
+ */
+ this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ insertBack!(T[])(init[]);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL Vector) with the elements from an input range.
+ *
+ * Params:
+ * R = Type of the initial range.
+ * init = Values to initialize the vector with.
+ * allocator = Allocator.
+ */
+ this(R)(R init, shared Allocator allocator = defaultAllocator)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ this(allocator);
+ insertBack(init);
+ }
+
+ /**
+ * Initializes this vector from another one.
+ *
+ * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
+ * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
+ * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
+ * storage, otherwise, the storage will be allocated with
+ * $(D_PARAM allocator) and all elements will be moved;
+ * $(D_PARAM init) will be destroyed at the end.
+ *
+ * If $(D_PARAM init) is passed by reference, it will be copied.
+ *
+ * Params:
+ * R = Source vector type.
+ * init = Source vector.
+ * allocator = Allocator.
+ */
+ this(R)(ref R init, shared Allocator allocator = defaultAllocator)
+ if (is(Unqual!R == Vector))
+ {
+ this(allocator);
+ insertBack(init[]);
+ }
+
+ /// Ditto.
+ this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
+ if (is(R == Vector))
+ {
+ this(allocator);
+ if (allocator is init.allocator)
+ {
+ // Just steal all references and the allocator.
+ this.data = init.data;
+ this.length_ = init.length_;
+ this.capacity_ = init.capacity_;
+
+ // Reset the source vector, so it can't destroy the moved storage.
+ init.length_ = init.capacity_ = 0;
+ init.data = null;
+ }
+ else
+ {
+ // Move each element.
+ reserve(init.length_);
+ moveEmplaceAll(init.data[0 .. init.length_], this.data[0 .. init.length_]);
+ this.length_ = init.length_;
+ // Destructor of init should destroy it here.
+ }
+ }
+
+ ///
+ @trusted @nogc unittest
+ {
+ auto v1 = Vector!int([1, 2, 3]);
+ auto v2 = Vector!int(v1);
+ assert(v1 == v2);
+
+ auto v3 = Vector!int(Vector!int([1, 2, 3]));
+ assert(v1 == v3);
+ assert(v3.length == 3);
+ assert(v3.capacity == 3);
+ }
+
+ private @trusted @nogc unittest // const constructor tests
+ {
+ auto v1 = const Vector!int([1, 2, 3]);
+ auto v2 = Vector!int(v1);
+ assert(v1.data !is v2.data);
+ assert(v1 == v2);
+
+ auto v3 = const Vector!int(Vector!int([1, 2, 3]));
+ assert(v1 == v3);
+ assert(v3.length == 3);
+ assert(v3.capacity == 3);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL Vector).
+ *
+ * Params:
+ * len = Initial length of the vector.
+ * init = Initial value to fill the vector with.
+ * allocator = Allocator.
+ */
+ this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
+ {
+ this(allocator);
+ reserve(len);
+ uninitializedFill(this.data[0 .. len], init);
+ length_ = len;
+ }
+
+ /// Ditto.
+ this(const size_t len, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ length = len;
+ }
+
+ /// Ditto.
+ this(shared Allocator allocator)
+ in
+ {
+ assert(allocator !is null);
+ }
+ body
+ {
+ allocator_ = allocator;
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([3, 8, 2]);
+
+ assert(v.capacity == 3);
+ assert(v.length == 3);
+ assert(v[0] == 3 && v[1] == 8 && v[2] == 2);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int(3, 5);
+
+ assert(v.capacity == 3);
+ assert(v.length == 3);
+ assert(v[0] == 5 && v[1] == 5 && v[2] == 5);
+ }
+
+ @safe unittest
+ {
+ auto v1 = Vector!int(defaultAllocator);
+ }
+
+ /**
+ * Destroys this $(D_PSYMBOL Vector).
+ */
+ ~this() @trusted
+ {
+ clear();
+ allocator.deallocate(this.data[0 .. capacity]);
+ }
+
+ /**
+ * Copies the vector.
+ */
+ this(this)
+ {
+ auto buf = this.data[0 .. this.length_];
+ this.length_ = capacity_ = 0;
+ this.data = null;
+ insertBack(buf);
+ }
+
+ /**
+ * Removes all elements.
+ */
+ void clear()
+ {
+ length = 0;
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([18, 20, 15]);
+ v.clear();
+ assert(v.length == 0);
+ assert(v.capacity == 3);
+ }
+
+ /**
+ * Returns: How many elements the vector can contain without reallocating.
+ */
+ @property size_t capacity() const
+ {
+ return capacity_;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v = Vector!int(4);
+ assert(v.capacity == 4);
+ }
+
+ /**
+ * Returns: Vector length.
+ */
+ @property size_t length() const
+ {
+ return length_;
+ }
+
+ /// Ditto.
+ size_t opDollar() const
+ {
+ return length;
+ }
+
+ /**
+ * Expands/shrinks the vector.
+ *
+ * Params:
+ * len = New length.
+ */
+ @property void length(const size_t len) @trusted
+ {
+ if (len == length)
+ {
+ return;
+ }
+ else if (len > length)
+ {
+ reserve(len);
+ initializeAll(this.data[length_ .. len]);
+ }
+ else
+ {
+ static if (hasElaborateDestructor!T)
+ {
+ const T* end = this.data + length_ - 1;
+ for (T* e = this.data + len; e != end; ++e)
+ {
+ destroy(*e);
+ }
+ }
+ }
+ length_ = len;
+ }
+
+ ///
+ unittest
+ {
+ Vector!int v;
+
+ v.length = 5;
+ assert(v.length == 5);
+ assert(v.capacity == 5);
+
+ v.length = 7;
+ assert(v.length == 7);
+ assert(v.capacity == 7);
+
+ assert(v[$ - 1] == 0);
+ v[$ - 1] = 3;
+ assert(v[$ - 1] == 3);
+
+ v.length = 0;
+ assert(v.length == 0);
+ assert(v.capacity == 7);
+ }
+
+ /**
+ * Reserves space for $(D_PARAM size) elements.
+ *
+ * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the
+ * function call does not cause a reallocation and the vector capacity is not
+ * affected.
+ *
+ * Params:
+ * size = Desired size.
+ */
+ void reserve(const size_t size) @trusted
+ {
+ if (capacity_ >= size)
+ {
+ return;
+ }
+ bool overflow;
+ immutable byteSize = mulu(size, T.sizeof, overflow);
+ assert(!overflow);
+
+ void[] buf = this.data[0 .. this.capacity_];
+ if (!allocator.reallocateInPlace(buf, byteSize))
+ {
+ buf = allocator.allocate(byteSize);
+ if (buf is null)
+ {
+ onOutOfMemoryErrorNoGC();
+ }
+ scope (failure)
+ {
+ allocator.deallocate(buf);
+ }
+ const T* end = this.data + this.length_;
+ for (T* src = this.data, dest = cast(T*) buf; src != end; ++src, ++dest)
+ {
+ moveEmplace(*src, *dest);
+ static if (hasElaborateDestructor!T)
+ {
+ destroy(*src);
+ }
+ }
+ allocator.deallocate(this.data[0 .. this.capacity_]);
+ this.data = cast(T*) buf;
+ }
+ this.capacity_ = size;
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ Vector!int v;
+ assert(v.capacity == 0);
+ assert(v.length == 0);
+
+ v.reserve(3);
+ assert(v.capacity == 3);
+ assert(v.length == 0);
+ }
+
+ /**
+ * Requests the vector to reduce its capacity to fit the $(D_PARAM size).
+ *
+ * The request is non-binding. The vector won't become smaller than the
+ * $(D_PARAM length).
+ *
+ * Params:
+ * size = Desired size.
+ */
+ void shrink(const size_t size) @trusted
+ {
+ if (capacity <= size)
+ {
+ return;
+ }
+ immutable n = max(length, size);
+ void[] buf = this.data[0 .. this.capacity_];
+ if (allocator.reallocateInPlace(buf, n * T.sizeof))
+ {
+ this.capacity_ = n;
+ }
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ Vector!int v;
+ assert(v.capacity == 0);
+ assert(v.length == 0);
+
+ v.reserve(5);
+ v.insertBack(1);
+ v.insertBack(3);
+ assert(v.capacity == 5);
+ assert(v.length == 2);
+ }
+
+ /**
+ * Returns: $(D_KEYWORD true) if the vector is empty.
+ */
+ @property bool empty() const
+ {
+ return length == 0;
+ }
+
+ /**
+ * Removes the value at the back of the vector.
+ *
+ * Returns: The number of elements removed
+ *
+ * Precondition: $(D_INLINECODE !empty).
+ */
+ void removeBack()
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ length = length - 1;
+ }
+
+ /**
+ * Removes $(D_PARAM howMany) elements from the vector.
+ *
+ * This method doesn't fail if it could not remove $(D_PARAM howMany)
+ * elements. Instead, if $(D_PARAM howMany) is greater than the vector
+ * length, all elements are removed.
+ *
+ * Params:
+ * howMany = How many elements should be removed.
+ *
+ * Returns: The number of elements removed
+ */
+ size_t removeBack(const size_t howMany)
+ out (removed)
+ {
+ assert(removed <= howMany);
+ }
+ body
+ {
+ immutable toRemove = min(howMany, length);
+
+ length = length - toRemove;
+
+ return toRemove;
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([5, 18, 17]);
+
+ assert(v.removeBack(0) == 0);
+ assert(v.removeBack(2) == 2);
+ assert(v.removeBack(3) == 1);
+ assert(v.removeBack(3) == 0);
+ }
+
+ /**
+ * Remove all elements beloning to $(D_PARAM r).
+ *
+ * Params:
+ * r = Range originally obtained from this vector.
+ *
+ * Returns: A range spanning the remaining elements in the array that
+ * initially were right after $(D_PARAM r).
+ *
+ * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
+ */
+ Range!T remove(Range!T r) @trusted
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ auto end = this.data + this.length;
+ moveAll(Range!T(this, r.end, end), Range!T(this, r.begin, end));
+ length = length - r.length;
+ return Range!T(this, r.begin, this.data + length);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]);
+
+ assert(v.remove(v[1 .. 3]).length == 4);
+ assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
+ assert(v.length == 5);
+
+ assert(v.remove(v[4 .. 4]).length == 1);
+ assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1);
+ assert(v.length == 5);
+
+ assert(v.remove(v[4 .. 5]).length == 0);
+ assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6);
+ assert(v.length == 4);
+
+ assert(v.remove(v[]).length == 0);
+
+ }
+
+ private void moveBack(R)(ref R el) @trusted
+ if (isImplicitlyConvertible!(R, T))
+ {
+ reserve(this.length + 1);
+ moveEmplace(el, *(this.data + this.length_));
+ ++this.length_;
+ }
+
+ /**
+ * Inserts the $(D_PARAM el) into the vector.
+ *
+ * Params:
+ * R = Type of the inserted value(s) (single value, range or static array).
+ * el = Value(s) should be inserted.
+ *
+ * Returns: The number of elements inserted.
+ */
+ size_t insertBack(R)(R el)
+ if (isImplicitlyConvertible!(R, T))
+ {
+ moveBack(el);
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBack(R)(ref R el) @trusted
+ if (isImplicitlyConvertible!(R, T))
+ {
+ reserve(this.length_ + 1);
+ emplace(this.data + this.length_, el);
+ ++this.length_;
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBack(R)(R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ static if (hasLength!R)
+ {
+ reserve(length + el.length);
+ }
+ size_t retLength;
+ foreach (e; el)
+ {
+ retLength += insertBack(e);
+ }
+ return retLength;
+ }
+
+ /// Ditto.
+ size_t insertBack(size_t R)(T[R] el)
+ {
+ return insertBack!(T[])(el[]);
+ }
+
+ /// Ditto.
+ alias insert = insertBack;
+
+ ///
+ unittest
+ {
+ struct TestRange
+ {
+ int counter = 6;
+
+ int front()
+ {
+ return counter;
+ }
+
+ void popFront()
+ {
+ counter -= 2;
+ }
+
+ bool empty()
+ {
+ return counter == 0;
+ }
+ }
+
+ Vector!int v1;
+
+ assert(v1.insertBack(5) == 1);
+ assert(v1.length == 1);
+ assert(v1.capacity == 1);
+ assert(v1.back == 5);
+
+ assert(v1.insertBack(TestRange()) == 3);
+ assert(v1.length == 4);
+ assert(v1.capacity == 4);
+ assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2);
+
+ assert(v1.insertBack([34, 234]) == 2);
+ assert(v1.length == 6);
+ assert(v1.capacity == 6);
+ assert(v1[4] == 34 && v1[5] == 234);
+ }
+
+ /**
+ * Inserts $(D_PARAM el) before or after $(D_PARAM r).
+ *
+ * Params:
+ * R = Type of the inserted value(s) (single value, range or static array).
+ * r = Range originally obtained from this vector.
+ * el = Value(s) should be inserted.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this).
+ */
+ size_t insertAfter(R)(Range!T r, R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ immutable oldLen = length;
+ immutable offset = r.end - this.data;
+ immutable inserted = insertBack(el);
+ bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
+ return inserted;
+ }
+
+ /// Ditto.
+ size_t insertAfter(size_t R)(Range!T r, T[R] el)
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ return insertAfter!(T[])(r, el[]);
+ }
+
+ /// Ditto.
+ size_t insertAfter(R)(Range!T r, auto ref R el)
+ if (isImplicitlyConvertible!(R, T))
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ immutable oldLen = length;
+ immutable offset = r.end - this.data;
+
+ static if (__traits(isRef, el))
+ {
+ insertBack(el);
+ }
+ else
+ {
+ moveBack(el);
+ }
+ bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
+
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertBefore(R)(Range!T r, R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ return insertAfter(Range!T(this, this.data, r.begin), el);
+ }
+
+ /// Ditto.
+ size_t insertBefore(size_t R)(Range!T r, T[R] el)
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ return insertBefore!(T[])(r, el[]);
+ }
+
+ /// Ditto.
+ size_t insertBefore(R)(Range!T r, auto ref R el)
+ if (isImplicitlyConvertible!(R, T))
+ in
+ {
+ assert(r.vector is &this);
+ assert(r.begin >= this.data);
+ assert(r.end <= this.data + length);
+ }
+ body
+ {
+ immutable oldLen = length;
+ immutable offset = r.begin - this.data;
+
+ static if (__traits(isRef, el))
+ {
+ insertBack(el);
+ }
+ else
+ {
+ moveBack(el);
+ }
+ bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]);
+
+ return 1;
+ }
+
+ ///
+ unittest
+ {
+ Vector!int v1;
+ v1.insertAfter(v1[], [2, 8]);
+ assert(v1[0] == 2);
+ assert(v1[1] == 8);
+ assert(v1.length == 2);
+
+ v1.insertAfter(v1[], [1, 2]);
+ assert(v1[0] == 2);
+ assert(v1[1] == 8);
+ assert(v1[2] == 1);
+ assert(v1[3] == 2);
+ assert(v1.length == 4);
+
+ v1.insertAfter(v1[0 .. 0], [1, 2]);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 2);
+ assert(v1[3] == 8);
+ assert(v1[4] == 1);
+ assert(v1[5] == 2);
+ assert(v1.length == 6);
+
+ v1.insertAfter(v1[0 .. 4], 9);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 2);
+ assert(v1[3] == 8);
+ assert(v1[4] == 9);
+ assert(v1[5] == 1);
+ assert(v1[6] == 2);
+ assert(v1.length == 7);
+ }
+
+ ///
+ unittest
+ {
+ Vector!int v1;
+ v1.insertBefore(v1[], [2, 8]);
+ assert(v1[0] == 2);
+ assert(v1[1] == 8);
+ assert(v1.length == 2);
+
+ v1.insertBefore(v1[], [1, 2]);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 2);
+ assert(v1[3] == 8);
+ assert(v1.length == 4);
+
+ v1.insertBefore(v1[0 .. 1], [1, 2]);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 1);
+ assert(v1[3] == 2);
+ assert(v1[4] == 2);
+ assert(v1[5] == 8);
+ assert(v1.length == 6);
+
+ v1.insertBefore(v1[2 .. $], 9);
+ assert(v1[0] == 1);
+ assert(v1[1] == 2);
+ assert(v1[2] == 9);
+ assert(v1[3] == 1);
+ assert(v1[4] == 2);
+ assert(v1[5] == 2);
+ assert(v1[6] == 8);
+ assert(v1.length == 7);
+ }
+
+ /**
+ * Assigns a value to the element with the index $(D_PARAM pos).
+ *
+ * Params:
+ * value = Value.
+ * pos = Position.
+ *
+ * Returns: Assigned value.
+ *
+ * Precondition: $(D_INLINECODE length > pos).
+ */
+ ref T opIndexAssign(ref T value, const size_t pos)
+ {
+ return opIndex(pos) = value;
+ }
+
+ @safe unittest
+ {
+ Vector!int a = Vector!int(1);
+ a[0] = 5;
+ assert(a[0] == 5);
+ }
+
+ /// Ditto.
+ T opIndexAssign(T value, const size_t pos)
+ {
+ return opIndexAssign(value, pos);
+ }
+
+ /// Ditto.
+ Range!T opIndexAssign(T value)
+ {
+ return opSliceAssign(value, 0, length);
+ }
+
+ /// Ditto.
+ Range!T opIndexAssign(ref T value)
+ {
+ return opSliceAssign(value, 0, length);
+ }
+
+ /**
+ * Assigns a range or a static array.
+ *
+ * Params:
+ * R = Range type or static array length.
+ * value = Value.
+ *
+ * Returns: Assigned value.
+ *
+ * Precondition: $(D_INLINECODE length == value.length).
+ */
+ Range!T opIndexAssign(R)(R value)
+ if (!isInfinite!R && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ return opSliceAssign!R(value, 0, length);
+ }
+
+ /// Ditto.
+ Range!T opIndexAssign(size_t R)(T[R] value)
+ {
+ return opSliceAssign!R(value, 0, length);
+ }
+
+ ///
+ @nogc unittest
+ {
+ auto v1 = Vector!int([12, 1, 7]);
+
+ v1[] = 3;
+ assert(v1[0] == 3);
+ assert(v1[1] == 3);
+ assert(v1[2] == 3);
+
+ v1[] = [7, 1, 12];
+ assert(v1[0] == 7);
+ assert(v1[1] == 1);
+ assert(v1[2] == 12);
+ }
+
+ /**
+ * Params:
+ * pos = Index.
+ *
+ * Returns: The value at a specified index.
+ *
+ * Precondition: $(D_INLINECODE length > pos).
+ */
+ ref inout(T) opIndex(const size_t pos) inout @trusted
+ in
+ {
+ assert(length > pos);
+ }
+ body
+ {
+ return *(this.data + pos);
+ }
+
+ /**
+ * Returns: Random access range that iterates over elements of the vector, in
+ * forward order.
+ */
+ Range!T opIndex() @trusted
+ {
+ return typeof(return)(this, this.data, this.data + length);
+ }
+
+ /// Ditto.
+ Range!(const T) opIndex() const @trusted
+ {
+ return typeof(return)(this, this.data, this.data + length);
+ }
+
+ ///
+ unittest
+ {
+ const v1 = Vector!int([6, 123, 34, 5]);
+
+ assert(v1[0] == 6);
+ assert(v1[1] == 123);
+ assert(v1[2] == 34);
+ assert(v1[3] == 5);
+ static assert(is(typeof(v1[0]) == const(int)));
+ static assert(is(typeof(v1[])));
+ }
+
+ /**
+ * Comparison for equality.
+ *
+ * Params:
+ * that = The vector to compare with.
+ *
+ * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false)
+ * otherwise.
+ */
+ bool opEquals()(auto ref typeof(this) that) @trusted
+ {
+ return equal(this.data[0 .. length], that.data[0 .. that.length]);
+ }
+
+ /// Ditto.
+ bool opEquals()(const auto ref typeof(this) that) const @trusted
+ {
+ return equal(this.data[0 .. length], that.data[0 .. that.length]);
+ }
+
+ /// Ditto.
+ bool opEquals(Range!T that)
+ {
+ return equal(opIndex(), that);
+ }
+
+ /**
+ * Comparison for equality.
+ *
+ * Params:
+ * R = Right hand side type.
+ * that = Right hand side vector range.
+ *
+ * Returns: $(D_KEYWORD true) if the vector and the range are equal,
+ * $(D_KEYWORD false) otherwise.
+ */
+ bool opEquals(R)(Range!R that) const
+ if (is(Unqual!R == T))
+ {
+ return equal(opIndex(), that);
+ }
+
+ ///
+ unittest
+ {
+ Vector!int v1, v2;
+ assert(v1 == v2);
+
+ v1.length = 1;
+ v2.length = 2;
+ assert(v1 != v2);
+
+ v1.length = 2;
+ v1[0] = v2[0] = 2;
+ v1[1] = 3;
+ v2[1] = 4;
+ assert(v1 != v2);
+
+ v2[1] = 3;
+ assert(v1 == v2);
+ }
+
+ /**
+ * Returns: The first element.
+ *
+ * Precondition: $(D_INLINECODE !empty).
+ */
+ @property ref inout(T) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *this.data;
+ }
+
+ ///
+ @safe unittest
+ {
+ auto v = Vector!int([5]);
+
+ assert(v.front == 5);
+
+ v.length = 2;
+ v[1] = 15;
+ assert(v.front == 5);
+ }
+
+ /**
+ * Returns: The last element.
+ *
+ * Precondition: $(D_INLINECODE !empty).
+ */
+ @property ref inout(T) back() inout @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return *(this.data + length - 1);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([5]);
+
+ assert(v.back == 5);
+
+ v.length = 2;
+ v[1] = 15;
+ assert(v.back == 15);
+ }
+
+ /**
+ * Params:
+ * i = Slice start.
+ * j = Slice end.
+ *
+ * Returns: A range that iterates over elements of the container from
+ * index $(D_PARAM i) up to (excluding) index $(D_PARAM j).
+ *
+ * Precondition: $(D_INLINECODE i <= j && j <= length).
+ */
+ Range!T opSlice(const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(this, this.data + i, this.data + j);
+ }
+
+ /// Ditto.
+ Range!(const T) opSlice(const size_t i, const size_t j) const @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ return typeof(return)(this, this.data + i, this.data + j);
+ }
+
+ ///
+ unittest
+ {
+ Vector!int v;
+ auto r = v[];
+ assert(r.length == 0);
+ assert(r.empty);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([1, 2, 3]);
+ auto r = v[];
+
+ assert(r.front == 1);
+ assert(r.back == 3);
+
+ r.popFront();
+ assert(r.front == 2);
+
+ r.popBack();
+ assert(r.back == 2);
+
+ assert(r.length == 1);
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([1, 2, 3, 4]);
+ auto r = v[1 .. 4];
+ assert(r.length == 3);
+ assert(r[0] == 2);
+ assert(r[1] == 3);
+ assert(r[2] == 4);
+
+ r = v[0 .. 0];
+ assert(r.length == 0);
+
+ r = v[4 .. 4];
+ assert(r.length == 0);
+ }
+
+ /**
+ * Slicing assignment.
+ *
+ * Params:
+ * R = Type of the assigned slice or length of the static array should be
+ * assigned.
+ * value = New value (single value, input range or static array).
+ * i = Slice start.
+ * j = Slice end.
+ *
+ * Returns: Slice with the assigned part of the vector.
+ *
+ * Precondition: $(D_INLINECODE i <= j && j <= length
+ * && value.length == j - i)
+ */
+ Range!T opSliceAssign(R)(R value, const size_t i, const size_t j) @trusted
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ assert(j - i == walkLength(value));
+ }
+ body
+ {
+ copy(value, this.data[i .. j]);
+ return opSlice(i, j);
+ }
+
+ /// Ditto.
+ Range!T opSliceAssign(size_t R)(T[R] value, const size_t i, const size_t j)
+ {
+ return opSliceAssign!(T[])(value[], i, j);
+ }
+
+ /// Ditto.
+ Range!T opSliceAssign(ref T value, const size_t i, const size_t j) @trusted
+ in
+ {
+ assert(i <= j);
+ assert(j <= length);
+ }
+ body
+ {
+ fill(this.data[i .. j], value);
+ return opSlice(i, j);
+ }
+
+ /// Ditto.
+ Range!T opSliceAssign(T value, const size_t i, const size_t j)
+ {
+ return opSliceAssign(value, i, j);
+ }
+
+ ///
+ @nogc @safe unittest
+ {
+ auto v1 = Vector!int([3, 3, 3]);
+ auto v2 = Vector!int([1, 2]);
+
+ v1[0 .. 2] = 286;
+ assert(v1[0] == 286);
+ assert(v1[1] == 286);
+ assert(v1[2] == 3);
+
+ v2[0 .. $] = v1[1 .. 3];
+ assert(v2[0] == 286);
+ assert(v2[1] == 3);
+
+ v1[0 .. 2] = [5, 8];
+ assert(v1[0] == 5);
+ assert(v1[1] == 8);
+ assert(v1[2] == 3);
+ }
+
+ /**
+ * Returns an array used internally by the vector to store its owned elements.
+ * The length of the returned array may differ from the size of the allocated
+ * memory for the vector: the array contains only initialized elements, but
+ * not the reserved memory.
+ *
+ * Returns: The array with elements of this vector.
+ */
+ inout(T[]) get() inout @trusted
+ {
+ return this.data[0 .. length];
+ }
+
+ ///
+ unittest
+ {
+ auto v = Vector!int([1, 2, 4]);
+ auto data = v.get();
+
+ assert(data[0] == 1);
+ assert(data[1] == 2);
+ assert(data[2] == 4);
+ assert(data.length == 3);
+
+ data = v[1 .. 2].get();
+ assert(data[0] == 2);
+ assert(data.length == 1);
+ }
+
+ /**
+ * Assigns another vector.
+ *
+ * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
+ * This vector will take the ownership over $(D_PARAM that)'s storage and
+ * the allocator.
+ *
+ * If $(D_PARAM that) is passed by reference, it will be copied.
+ *
+ * Params:
+ * R = Content type.
+ * that = The value should be assigned.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(R)(ref R that)
+ if (is(Unqual!R == Vector))
+ {
+ return this = that[];
+ }
+
+ /// Ditto.
+ ref typeof(this) opAssign(R)(R that) @trusted
+ if (is(R == Vector))
+ {
+ swap(this.data, that.data);
+ swap(this.length_, that.length_);
+ swap(this.capacity_, that.capacity_);
+ swap(this.allocator_, that.allocator_);
+ return this;
+ }
+
+ /**
+ * Assigns a range to the vector.
+ *
+ * Params:
+ * R = Content type.
+ * that = The value should be assigned.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(R)(R that)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ length = 0;
+ insertBack(that);
+ return this;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v1 = const Vector!int([5, 15, 8]);
+ Vector!int v2;
+ v2 = v1;
+ assert(v1 == v2);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v1 = const Vector!int([5, 15, 8]);
+ Vector!int v2;
+ v2 = v1[0 .. 2];
+ assert(equal(v1[0 .. 2], v2[]));
+ }
+
+ // Move assignment.
+ private @safe @nogc unittest
+ {
+ Vector!int v1;
+ v1 = Vector!int([5, 15, 8]);
+ }
+
+ /**
+ * Assigns a static array.
+ *
+ * Params:
+ * R = Static array size.
+ * that = Values to initialize the vector with.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(size_t R)(T[R] that)
+ {
+ return opAssign!(T[])(that[]);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto v1 = Vector!int([5, 15, 8]);
+ Vector!int v2;
+
+ v2 = [5, 15, 8];
+ assert(v1 == v2);
+ }
+
+ mixin DefaultAllocator;
+}
+
+///
+unittest
+{
+ auto v = Vector!int([5, 15, 8]);
+
+ assert(v.front == 5);
+ assert(v[1] == 15);
+ assert(v.back == 8);
+
+ auto r = v[];
+ r[0] = 7;
+ assert(r.front == 7);
+ assert(r.front == v.front);
+}
+
+@nogc unittest
+{
+ const v1 = Vector!int();
+ const Vector!int v2;
+ const v3 = Vector!int([1, 5, 8]);
+ static assert(is(PointerTarget!(typeof(v3.data)) == const(int)));
+}
+
+@nogc unittest
+{
+ // Test that const vectors return usable ranges.
+ auto v = const Vector!int([1, 2, 4]);
+ auto r1 = v[];
+
+ assert(r1.back == 4);
+ r1.popBack();
+ assert(r1.back == 2);
+ r1.popBack();
+ assert(r1.back == 1);
+ r1.popBack();
+ assert(r1.length == 0);
+
+ static assert(!is(typeof(r1[0] = 5)));
+ static assert(!is(typeof(v[0] = 5)));
+
+ const r2 = r1[];
+ static assert(is(typeof(r2[])));
+}
+
+@nogc unittest
+{
+ Vector!int v1;
+ const Vector!int v2;
+
+ auto r1 = v1[];
+ auto r2 = v1[];
+
+ assert(r1.length == 0);
+ assert(r2.empty);
+ assert(r1 == r2);
+
+ v1.insertBack([1, 2, 4]);
+ assert(v1[] == v1);
+ assert(v2[] == v2);
+ assert(v2[] != v1);
+ assert(v1[] != v2);
+ assert(v1[].equal(v1[]));
+ assert(v2[].equal(v2[]));
+ assert(!v1[].equal(v2[]));
+}
+
+@nogc unittest
+{
+ struct MutableEqualsStruct
+ {
+ int opEquals(typeof(this) that) @nogc
+ {
+ return true;
+ }
+ }
+ struct ConstEqualsStruct
+ {
+ int opEquals(const typeof(this) that) const @nogc
+ {
+ return true;
+ }
+ }
+ auto v1 = Vector!ConstEqualsStruct();
+ auto v2 = Vector!ConstEqualsStruct();
+ assert(v1 == v2);
+ assert(v1[] == v2);
+ assert(v1 == v2[]);
+ assert(v1[].equal(v2[]));
+
+ auto v3 = const Vector!ConstEqualsStruct();
+ auto v4 = const Vector!ConstEqualsStruct();
+ assert(v3 == v4);
+ assert(v3[] == v4);
+ assert(v3 == v4[]);
+ assert(v3[].equal(v4[]));
+
+ auto v7 = Vector!MutableEqualsStruct(1, MutableEqualsStruct());
+ auto v8 = Vector!MutableEqualsStruct(1, MutableEqualsStruct());
+ assert(v7 == v8);
+ assert(v7[] == v8);
+ assert(v7 == v8[]);
+ assert(v7[].equal(v8[]));
+}
+
+@nogc unittest
+{
+ struct SWithDtor
+ {
+ ~this() @nogc
+ {
+ }
+ }
+ auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors.
+}
+
+private unittest
+{
+ class A
+ {
+ }
+ A a1, a2;
+ auto v1 = Vector!A([a1, a2]);
+}
+
+private @safe @nogc unittest
+{
+ auto v = Vector!int([5, 15, 8]);
+ {
+ size_t i;
+
+ foreach (e; v)
+ {
+ assert(i != 0 || e == 5);
+ assert(i != 1 || e == 15);
+ assert(i != 2 || e == 8);
+ ++i;
+ }
+ assert(i == 3);
+ }
+ {
+ size_t i = 3;
+
+ foreach_reverse (e; v)
+ {
+ --i;
+ assert(i != 2 || e == 8);
+ assert(i != 1 || e == 15);
+ assert(i != 0 || e == 5);
+ }
+ assert(i == 0);
+ }
+}
diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d
index 024a0e4..e55829c 100644
--- a/source/tanya/math/mp.d
+++ b/source/tanya/math/mp.d
@@ -12,11 +12,11 @@
*/
module tanya.math.mp;
-import core.exception;
import std.algorithm;
+import std.ascii;
import std.range;
import std.traits;
-import tanya.math;
+import tanya.container.vector;
import tanya.memory;
/**
@@ -36,8 +36,8 @@ enum Sign : bool
*/
struct Integer
{
- private size_t size;
- package ubyte* rep;
+ package digit[] rep;
+ package ptrdiff_t size;
package Sign sign;
pure nothrow @safe @nogc invariant
@@ -45,6 +45,16 @@ struct Integer
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.
*
@@ -59,20 +69,21 @@ struct Integer
if (isIntegral!T)
{
this(allocator);
- assign(value);
+ this = value;
}
/// Ditto.
- this(const ref Integer value, shared Allocator allocator = defaultAllocator)
- nothrow @safe @nogc
+ this(T)(ref T value, shared Allocator allocator = defaultAllocator)
+ if (is(Unqual!T == Integer))
{
this(allocator);
- assign(value);
+ this = value;
}
/// Ditto.
- this(Integer value, shared Allocator allocator = defaultAllocator)
+ this(T)(T value, shared Allocator allocator = defaultAllocator)
nothrow @safe @nogc
+ if (is(T == Integer))
{
this(allocator);
if (allocator is value.allocator)
@@ -86,7 +97,7 @@ struct Integer
}
else
{
- assign(value);
+ this = value;
}
}
@@ -101,22 +112,6 @@ struct Integer
this.allocator_ = allocator;
}
- private @nogc unittest
- {
- {
- auto integer = Integer(79);
- assert(integer.length == 1);
- assert(integer.rep[0] == 79);
- assert(!integer.sign);
- }
- {
- auto integer = Integer(-2);
- assert(integer.length == 1);
- assert(integer.rep[0] == 2);
- assert(integer.sign);
- }
- }
-
/**
* Constructs the integer from a sign-magnitude $(D_KEYWORD ubyte) range.
*
@@ -128,26 +123,41 @@ struct Integer
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
- this(R)(const Sign sign, R value, shared Allocator allocator = defaultAllocator)
- @trusted
- if (isInputRange!R && hasLength!R && is(Unqual!(ElementType!R) == ubyte))
+ this(R)(const Sign sign,
+ R value,
+ shared Allocator allocator = defaultAllocator)
+ if (isBidirectionalRange!R && hasLength!R
+ && is(Unqual!(ElementType!R) == ubyte))
{
this(allocator);
- while (!value.empty && value.front == 0)
+ grow(value.length / (digitBitCount / 8) + 1);
+
+ int bit, delta;
+
+ for (; !value.empty; ++this.size)
{
- value.popFront();
+ 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;
+ }
}
- this.rep = allocator.resize(this.rep[0 .. this.size], value.length).ptr;
- this.size = value.length;
- this.sign = sign;
- value.copy(this.rep[0 .. this.size].retro);
}
- private @nogc unittest
+ nothrow @safe @nogc unittest
{
- ubyte[5] range = [ 0x02, 0x11, 0x00, 0x00, 0x01 ];
+ ubyte[8] range = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
auto integer = Integer(Sign.positive, range[]);
- assert(equal(range[].retro, integer.rep[0 .. integer.size]));
+ assert(integer == 7383520307673030126);
}
/**
@@ -155,9 +165,9 @@ struct Integer
*/
this(this) nothrow @trusted @nogc
{
- auto tmp = allocator.resize!ubyte(null, this.size);
+ auto tmp = allocator.resize!digit(null, this.size);
this.rep[0 .. this.size].copy(tmp);
- this.rep = tmp.ptr;
+ this.rep = tmp;
}
/**
@@ -165,231 +175,344 @@ struct Integer
*/
~this() nothrow @trusted @nogc
{
- allocator.deallocate(this.rep[0 .. this.size]);
+ allocator.resize(this.rep, 0);
}
- /*
- * Figures out the minimum amount of space this value will take
- * up in bytes and resizes the internal storage. Sets the sign.
+ /**
+ * Returns: Integer byte length.
+ */
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return (countBits() + 7) / 8; // Round up.
+ }
+
+ /**
+ * Assigns a new value.
+ *
+ * Params:
+ * T = Value type.
+ * value = Value.
+ *
+ * Returns: $(D_KEYWORD this).
*/
- private void assign(T)(const ref T value) @trusted
+ ref Integer opAssign(T)(const T value)
if (isIntegral!T)
{
- ubyte size = ulong.sizeof;
- ulong mask;
+ rep[0 .. this.size].fill(digit.init);
+ grow(digitBitCount / 8 + 1);
- this.sign = Sign.positive; // Reset the sign.
static if (isSigned!T)
{
- const absolute = value.abs;
- }
- else
- {
- alias absolute = value;
- }
- for (mask = 0xff00000000000000; mask >= 0xff; mask >>= 8)
- {
- if (absolute & mask)
+ ulong absolute;
+ if (value >= 0)
{
- break;
+ absolute = value;
+ this.sign = Sign.positive;
+ }
+ else
+ {
+ absolute = -value;
+ this.sign = Sign.negative;
}
- --size;
}
-
- this.rep = allocator.resize(this.rep[0 .. this.size], size).ptr;
- this.size = size;
- static if (isSigned!T)
+ else
{
- this.sign = value < 0 ? Sign.negative : Sign.positive;
+ ulong absolute = value;
+ this.sign = Sign.positive;
}
- /* 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. */
- mask = 0xff;
- ubyte shift;
- for (size_t i; i < this.size; ++i, mask <<= 8, shift += 8)
+ for (this.size = 0; absolute; absolute >>= digitBitCount, ++this.size)
{
- this.rep[i] = cast(ubyte) ((absolute & mask) >> shift);
+ this.rep[this.size] = absolute & mask;
}
+
+ return this;
}
- private void assign(const ref Integer value) nothrow @trusted @nogc
+ /// Ditto.
+ ref Integer opAssign(T)(ref T value) @trusted
+ if (is(Unqual!T == Integer))
{
- this.rep = allocator.resize(this.rep[0 .. this.size], value.size).ptr;
+ this.rep = allocator.resize(this.rep, value.size);
+ value.rep[0 .. value.size].copy(this.rep[0 .. value.size]);
this.size = value.size;
this.sign = value.sign;
- value.rep[0 .. value.size].copy(this.rep[0 .. this.size]);
+
+ return this;
}
- /**
- * Returns: Integer size.
- */
- @property size_t length() const pure nothrow @safe @nogc
+ /// Ditto.
+ ref Integer opAssign(T)(T value) nothrow @safe @nogc
+ if (is(T == Integer))
{
- return this.size;
+ swap(this.rep, value.rep);
+ swap(this.sign, value.sign);
+ swap(this.size, value.size);
+ swap(this.allocator_, value.allocator_);
+ return this;
}
/**
- * Assigns a new value.
+ * Casting.
*
* Params:
- * T = Value type.
- * value = Value.
+ * T = Target type.
*
- * Returns: $(D_KEYWORD this).
+ * Returns: Converted value.
*/
- ref Integer opAssign(T)(const T value)
- if (isIntegral!T)
+ T opCast(T : bool)() const
{
- assign(value);
- return this;
+ return this.size > 0;
}
/// Ditto.
- ref Integer opAssign(const ref Integer value) nothrow @safe @nogc
+ T opCast(T)() const
+ if (isIntegral!T && isUnsigned!T)
{
- assign(value);
- return this;
+ 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.
- ref Integer opAssign(Integer value) nothrow @safe @nogc
+ T opCast(T)() const
+ if (isIntegral!T && isSigned!T)
{
- swap(this.rep, value.rep);
- swap(this.sign, value.sign);
- swap(this.size, value.size);
- return this;
+ return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this;
}
- private @nogc unittest
+ ///
+ @safe @nogc unittest
{
- auto integer = Integer(1019);
- assert(integer.length == 2);
- assert(integer.rep[1] == 0b00000011 && integer.rep[0] == 0b11111011);
+ auto integer = Integer(79);
+ assert(cast(ushort) integer == 79);
+
+ integer = -79;
+ assert(cast(short) integer == -79);
+
+ integer = 4294967295;
+ assert(cast(long) integer == 4294967295);
- integer = 3337;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0b00001101 && integer.rep[0] == 0b00001001);
+ integer = -4294967295;
+ assert(cast(long) integer == -4294967295);
- integer = 688;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0b00000010 && integer.rep[0] == 0b10110000);
+ integer = long.min;
+ assert(cast(long) integer == long.min);
+
+ integer = long.min + 1;
+ assert(cast(long) integer == long.min + 1);
integer = 0;
- assert(integer.length == 0);
+ assert(cast(long) integer == 0);
}
- /*
- * Extend the space for this by 1 byte and set the LSB to 1.
+ /* 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
*/
- private void expand() nothrow @trusted @nogc
+ void contract() nothrow @safe @nogc
{
- rep = allocator.resize(this.rep[0 .. this.size], this.size + 1).ptr;
- this.rep[this.size] = 0x01;
- ++this.size;
+ /* 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;
+ }
}
- /*
- * Go through this and see how many of the left-most bytes are unused.
- * Remove them and resize this appropriately.
- */
- private void contract() nothrow @trusted @nogc
+ private void grow(const size_t size) nothrow @trusted @nogc
{
- const i = this.rep[0 .. this.size]
- .retro
- .countUntil!((const ref a) => a != 0);
+ if (this.rep.length >= size)
+ {
+ return;
+ }
+ const oldLength = this.rep.length;
+ allocator.resize(this.rep, size);
+ this.rep[oldLength .. $].fill(digit.init);
+ }
- if (i > 0)
+ private size_t countBits() const pure nothrow @safe @nogc
+ {
+ if (this.size == 0)
{
- this.rep = allocator.resize(this.rep[0 .. this.size], this.size - i).ptr;
- this.size -= i;
+ return 0;
}
- else if (i == -1)
+ auto r = (this.size - 1) * digitBitCount;
+ digit q = this.rep[this.size - 1];
+
+ while (q > 0)
{
- this.sign = Sign.positive;
- this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr;
- this.size = 0;
+ ++r;
+ q >>= (cast(digit) 1);
}
+ return r;
}
- private void add(const ref Integer summand) nothrow @trusted @nogc
+ private void add(ref const Integer summand, ref Integer sum)
+ const nothrow @safe @nogc
{
- if (summand.length > this.length)
+ const(digit)[] max, min;
+
+ if (this.size > summand.size)
{
- this.rep = allocator.resize!ubyte(this.rep[0 .. this.size], summand.size).ptr;
- this.rep[this.size .. summand.size].initializeAll();
+ 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);
- this.size = summand.size;
+ 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();
}
- bool carry;
- size_t i;
- size_t j;
- do
+ // Copy higher digests if one of the summands is greater than another
+ // one.
+ for (; !max.empty; max.popFront(), result.popFront())
{
- uint sum;
- if (j < summand.size)
- {
- sum = this.rep[i] + summand.rep[j] + carry;
- ++j;
- }
- else
- {
- sum = this.rep[i] + carry;
- }
+ 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 nothrow @safe @nogc
+ {
+ sum.grow(this.size + 2);
+
+ sum.rep[0] = this.rep[0] + summand;
+ auto carry = sum.rep[0] >> digitBitCount;
+ sum.rep[0] &= mask;
- carry = sum > 0xff;
- this.rep[i] = cast(ubyte) sum;
+ 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;
}
- while (++i < this.size);
+ sum.rep[i++] = carry;
- if (carry)
+ for (; i < sum.size; ++i)
{
- // Still overflowed; allocate more space
- expand();
+ sum.rep[i] = 0;
}
+ sum.size = this.size + 1;
+ sum.contract();
}
- private void subtract(const ref Integer subtrahend) nothrow @trusted @nogc
+ private void subtract(ref const Integer subtrahend, ref Integer difference)
+ const nothrow @safe @nogc
{
+ difference.grow(this.size);
+
+ const oldSize = difference.size;
+ difference.size = this.size;
+
size_t i;
- size_t j;
- bool borrow;
+ digit carry;
- while (i < this.size)
+ for (i = 0; i < subtrahend.size; ++i)
{
- int difference;
+ difference.rep[i] = (this.rep[i] - subtrahend.rep[i]) - carry;
+ carry = difference.rep[i] >> (cast(digit) (8 * digit.sizeof) - 1);
+ difference.rep[i] &= mask;
+ }
- if (j < subtrahend.size)
- {
- difference = this.rep[i] - subtrahend.rep[j] - borrow;
- ++j;
- }
- else
- {
- difference = this.rep[i] - borrow;
- }
- borrow = difference < 0;
- this.rep[i] = cast(ubyte) difference;
+ // 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 nothrow @safe @nogc
+ {
+ difference.grow(this.size);
+
+ const oldSize = difference.size;
+
+ difference.sign = this.sign;
+ difference.size = this.size;
- ++i;
+ 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;
}
- if (borrow && i < this.size && this.rep[i])
+ for (; i < oldSize; ++i)
{
- --this.rep[i];
+ difference.rep[i] = 0;
}
- contract();
+ difference.contract();
}
- private int compare(const ref Integer that) const nothrow @trusted @nogc
+ // Compare the magnitude.
+ private int compare(ref const Integer that) const pure nothrow @safe @nogc
{
- if (length > that.length)
+ if (this.size > that.size)
{
return 1;
}
- else if (length < that.length)
+ else if (this.size < that.size)
{
return -1;
}
@@ -402,18 +525,33 @@ struct Integer
* 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)(const auto ref I that) const
+ int opCmp(I : Integer)(auto ref const I that) const
{
if (this.sign != that.sign)
{
- return this.sign ? -1 : 1;
+ if (this.sign == Sign.negative)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ if (this.sign == Sign.negative)
+ {
+ return that.compare(this);
+ }
+ else
+ {
+ return compare(that);
}
- return compare(that);
}
///
@@ -434,7 +572,7 @@ struct Integer
}
/// Ditto.
- int opCmp(I)(const auto ref I that) const
+ int opCmp(I)(const I that) const
if (isIntegral!I)
{
if (that < 0 && !this.sign)
@@ -445,7 +583,7 @@ struct Integer
{
return -1;
}
- else if (this.length > I.sizeof)
+ else if (this.size > I.sizeof)
{
return this.sign ? -1 : 1;
}
@@ -475,11 +613,12 @@ struct Integer
/**
* Params:
+ * I = Comparand type.
* that = The second integer.
*
* Returns: Whether the two integers are equal.
*/
- bool opEquals(I)(const auto ref I that) const
+ bool opEquals(I)(auto ref const I that) const
if (is(I : Integer) || isIntegral!I)
{
return opCmp!I(that) == 0;
@@ -503,160 +642,112 @@ struct Integer
*
* Returns: $(D_KEYWORD this).
*/
- ref Integer opOpAssign(string op : "+")(const auto ref Integer operand)
+ ref Integer opOpAssign(string op : "+")(auto ref const Integer operand)
{
if (this.sign == operand.sign)
{
- add(operand);
+ add(operand, this);
+ }
+ else if (compare(operand) < 0)
+ {
+ this.sign = operand.sign;
+ operand.subtract(this, this);
}
else
{
- if (this >= operand)
- {
- subtract(operand);
- }
- else
- {
- // Swap the operands.
- auto tmp = Integer(this, this.allocator);
- this = Integer(operand, this.allocator);
- subtract(tmp);
- this.sign = operand.sign;
- }
-
+ subtract(operand, this);
}
return this;
}
- private @nogc unittest
+ ///
+ unittest
{
{
auto h1 = Integer(1019);
auto h2 = Integer(3337);
h1 += h2;
- assert(h1.length == 2);
- assert(h1.rep[1] == 0x11 && h1.rep[0] == 0x04);
+ assert(h1 == 4356);
}
{
auto h1 = Integer(4356);
auto h2 = Integer(2_147_483_647);
- ubyte[4] expected = [ 0x03, 0x11, 0x00, 0x80 ];
h1 += h2;
- assert(h1.rep[0 .. h1.size] == expected);
+ assert(h1 == 2147488003);
}
{
auto h1 = Integer(2147488003L);
auto h2 = Integer(2_147_483_647);
- ubyte[5] expected = [ 0x02, 0x11, 0x00, 0x00, 0x01 ];
h1 += h2;
- assert(h1.rep[0 .. h1.size] == expected);
+ assert(h1 == 4294971650);
}
}
/// Ditto.
- ref Integer opOpAssign(string op : "-")(const auto ref Integer operand)
+ ref Integer opOpAssign(string op : "-")(auto ref const Integer operand)
{
- if (operand.sign == this.sign)
+ if (this.sign != operand.sign)
{
- if (this >= operand)
- {
- subtract(operand);
- }
- else
- {
- // Swap the operands.
- auto tmp = Integer(this, this.allocator);
- this = Integer(operand, this.allocator);
- subtract(tmp);
-
- if (operand.sign || this.size == 0)
- {
- this.sign = Sign.positive;
- }
- else
- {
- this.sign = Sign.negative;
- }
- }
+ add(operand, this);
+ }
+ else if (compare(operand) >= 0)
+ {
+ subtract(operand, this);
}
else
{
- add(operand);
+ operand.subtract(this, this);
+ this.sign = this.sign ? Sign.positive : Sign.negative;
}
return this;
}
- private @nogc unittest
+ ///
+ unittest
{
{
auto h1 = Integer(3);
auto h2 = Integer(4);
h1 -= h2;
- assert(h1.length == 1);
- assert(h1.rep[0] == 0x01);
- assert(h1.sign == Sign.negative);
+ assert(h1 == -1);
}
{
auto h1 = Integer(8589934590L);
auto h2 = Integer(2147483647);
- ubyte[5] expected = [ 0xff, 0xff, 0xff, 0x7f, 0x01 ];
-
h1 -= h2;
- assert(h1.rep[0 .. h1.size] == expected);
+ assert(h1 == 6442450943);
}
{
auto h1 = Integer(6442450943);
auto h2 = Integer(4294967294);
- ubyte[4] expected = [ 0x01, 0x00, 0x00, 0x80 ];
h1 -= h2;
- assert(h1.rep[0 .. h1.size] == expected);
+ assert(h1 == 2147483649);
}
{
auto h1 = Integer(2147483649);
auto h2 = Integer(h1);
h1 -= h2;
- assert(h1.length == 0);
+ assert(h1 == 0);
}
}
/// Ditto.
- ref Integer opOpAssign(string op : "*")(const auto ref Integer operand) @trusted
+ ref Integer opOpAssign(string op : "*")(auto ref const Integer operand)
{
- size_t i;
- if (this.length == 0)
- {
- return this;
- }
- else if (operand.length == 0)
- {
- this = 0;
- return this;
- }
- auto temp = Integer(this, this.allocator);
- auto sign = this.sign != operand.sign;
+ const digits = this.size + operand.size + 1;
- this = 0;
- do
+ multiply(operand, this, digits);
+
+ if (this.size > 0)
{
- for (ubyte mask = 0x01; mask; mask <<= 1)
- {
- if (mask & operand.rep[i])
- {
- add(temp);
- }
- temp <<= 1;
- }
- ++i;
+ this.sign = this.sign == operand.sign ? Sign.positive : Sign.negative;
}
- while (i < operand.size);
-
- this.sign = sign ? Sign.negative : Sign.positive;
return this;
}
///
- @safe @nogc unittest
+ nothrow @safe @nogc unittest
{
auto h1 = Integer(123);
auto h2 = Integer(456);
@@ -664,284 +755,155 @@ struct Integer
assert(h1 == 56088);
}
- private @nogc unittest
- {
- assert((Integer(1) * Integer()).length == 0);
- }
-
/// Ditto.
- ref Integer opOpAssign(string op : "^^")(const auto ref Integer operand)
- @trusted
+ ref Integer opOpAssign(string op : "/")(auto ref const Integer operand)
+ in
{
- size_t i;
-
- auto tmp1 = Integer(this, this.allocator);
- this = 1;
-
- do
- {
- for (ubyte mask = 0x01; mask; mask <<= 1)
- {
- if (operand.rep[i] & mask)
- {
- this *= tmp1;
- }
- // Square tmp1
- auto tmp2 = tmp1;
- tmp1 *= tmp2;
- }
- ++i;
- }
- while (i < operand.size);
-
- return this;
+ assert(operand.length > 0, "Division by zero.");
}
-
- private @nogc unittest
+ body
{
- auto h1 = Integer(2);
- auto h2 = Integer(4);
-
- h1 ^^= h2;
- assert(h1.length == 1);
- assert(h1.rep[0] == 0x10);
-
- h1 = Integer(2342);
- h1 ^^= h2;
- ubyte[6] expected = [ 0x10, 0x31, 0x9c, 0xab, 0x5c, 0x1b ];
- assert(h1.rep[0 .. h1.size] == expected);
+ divide(operand, this);
+ return this;
}
- /// Ditto.
- ref Integer opOpAssign(string op)(const auto ref Integer operand) @trusted
- if ((op == "%") || (op == "/"))
+ /// Ditto.
+ ref Integer opOpAssign(string op : "%")(auto ref const Integer operand)
in
{
assert(operand.length > 0, "Division by zero.");
}
body
{
- auto divisor = Integer(operand, this.allocator);
- size_t bitSize;
-
- // First, left-shift divisor until it's >= than the dividend
- while (compare(divisor) > 0)
- {
- divisor <<= 1;
- ++bitSize;
- }
- static if (op == "/")
- {
- auto quotient = allocator.resize!ubyte(null, bitSize / 8 + 1);
- quotient.initializeAll();
- }
-
- // bitPosition keeps track of which bit, of the quotient,
- // is being set or cleared on the current operation.
- auto bitPosition = 8 - (bitSize % 8) - 1;
- do
- {
- if (compare(divisor) >= 0)
- {
- subtract(divisor); // dividend -= divisor
- static if (op == "/")
- {
- quotient[$ - 1 - bitPosition / 8] |= 0x80 >> (bitPosition % 8);
- }
- }
- if (bitSize != 0)
- {
- divisor >>= 1;
- }
- ++bitPosition;
- }
- while (bitSize--);
-
- static if (op == "/")
- {
- allocator.resize(this.rep[0 .. this.size], 0);
- this.rep = quotient.ptr;
- this.size = quotient.length;
- this.sign = this.sign == divisor.sign ? Sign.positive : Sign.negative;
- contract();
- }
-
+ divide(operand, null, this);
return this;
}
- private @nogc unittest
+ nothrow @safe @nogc unittest
{
auto h1 = Integer(18);
auto h2 = Integer(4);
h1 %= h2;
- assert(h1.length == 1);
- assert(h1.rep[0] == 0x02);
+ assert(h1 == 2);
h1 = 8;
h1 %= h2;
- assert(h1.length == 0);
+ assert(h1 == 0);
h1 = 7;
h1 %= h2;
- assert(h1.length == 1);
- assert(h1.rep[0] == 0x03);
+ assert(h1 == 3);
h1 = 56088;
h2 = 456;
h1 /= h2;
- assert(h1.length == 1);
- assert(h1.rep[0] == 0x7b);
+ assert(h1 == 123);
}
/// Ditto.
- ref Integer opOpAssign(string op : ">>")(const size_t operand) @trusted
+ ref Integer opOpAssign(string op : ">>")(const size_t operand)
{
- const step = operand / 8;
-
- if (step >= this.length)
+ if (operand == 0)
{
- this.rep = allocator.resize(this.rep[0 .. this.size], 0).ptr;
- this.size = 0;
return this;
}
-
- size_t i, j;
- ubyte carry;
- const bit = operand % 8;
- const delta = 8 - bit;
-
- for (j = step; j < length - 1; ++i, ++j)
+ if (operand >= digitBitCount)
{
- carry = cast(ubyte) (this.rep[i + 1] << delta);
- this.rep[i] = (this.rep[i] >> bit) | carry;
+ shiftRight(operand / digitBitCount);
}
- this.rep[i] = this.rep[j] >> bit;
- size_t newSize = length - step;
- if (this.rep[i] == 0)
+ const bit = cast(digit) (operand % digitBitCount);
+ if (bit != 0)
{
- --newSize;
- }
- this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr;
- this.size = newSize;
+ 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;
}
- private @nogc unittest
+ ///
+ nothrow @safe @nogc unittest
{
auto integer = Integer(4294967294);
integer >>= 10;
- assert(integer.length == 3);
- assert(integer.rep[2] == 0x3f && integer.rep[1] == 0xff && integer.rep[0] == 0xff);
+ assert(integer == 4194303);
integer = 27336704;
integer >>= 1;
- assert(integer.length == 3);
- assert(integer.rep[2] == 0xd0 && integer.rep[1] == 0x90 && integer.rep[0] == 0x00);
+ assert(integer == 13668352);
integer = 4294967294;
integer >>= 20;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0x0f && integer.rep[0] == 0xff);
+ assert(integer == 4095);
integer >>= 0;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0x0f && integer.rep[0] == 0xff);
+ assert(integer == 4095);
integer >>= 20;
- assert(integer.length == 0);
+ assert(integer == 0);
integer >>= 2;
- assert(integer.length == 0);
+ assert(integer == 0);
integer = 1431655765;
integer >>= 16;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0x55 && integer.rep[0] == 0x55);
+ assert(integer == 21845);
integer >>= 16;
- assert(integer.length == 0);
+ assert(integer == 0);
}
/// Ditto.
- ref Integer opOpAssign(string op : "<<")(const size_t operand) @trusted
+ ref Integer opOpAssign(string op : "<<")(const size_t operand)
{
- auto i = this.length;
- size_t j;
- size_t newSize;
- const bit = operand % 8;
- const delta = 8 - bit;
- const step = operand / 8;
-
- auto carry = cast(ubyte) (this.rep[this.size - 1] >> delta);
- if (carry != 0)
+ const step = operand / digitBitCount;
+ if (this.rep.length < this.size + step + 1)
{
- newSize = length + step + 1;
- this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr;
- this.size = newSize;
- j = newSize - 1;
- this.rep[j] = carry;
+ grow(this.size + step + 1);
}
- else
+ if (operand >= digitBitCount)
{
- newSize = length + step;
- this.rep = allocator.resize(this.rep[0 .. this.size], newSize).ptr;
- this.size = j = newSize;
+ shiftLeft(step);
}
- --i, --j;
- for (; i > 0; --i, --j)
+ const bit = cast(digit) (operand % digitBitCount);
+ if (bit != 0)
{
- this.rep[i] = cast(ubyte) (this.rep[j] << bit) | (this.rep[j - 1] >> delta);
- }
- this.rep[i] = cast(ubyte) (this.rep[j] << bit);
- this.rep[0 .. step].fill(cast(ubyte) 0);
+ const mask = ((cast(digit) 1) << bit) - 1;
+ const shift = digitBitCount - bit;
+ digit carry;
- return this;
- }
-
- private @nogc unittest
- {
- auto integer = Integer(4294967295);
- ubyte[5] expected = [ 0xfe, 0xff, 0xff, 0xff, 0x01 ];
- integer <<= 1;
- assert(integer.rep[0 .. integer.size] == expected);
- }
+ foreach (ref d; this.rep[0 .. this.size])
+ {
+ const newCarry = (d >> shift) & mask;
+ d = ((d << bit) | carry) & this.mask;
+ carry = newCarry;
+ }
- private void decrement() nothrow @trusted @nogc
- in
- {
- assert(this.length > 0);
- }
- body
- {
- for (ubyte* p = this.rep; p < this.rep + this.size; ++p)
- {
- --*p;
- if (*p != ubyte.max)
+ if (carry != 0)
{
- break;
+ this.rep[this.size++] = carry;
}
}
- contract();
+ this.contract();
+ return this;
}
- private void increment() nothrow @trusted @nogc
+ ///
+ nothrow @safe @nogc unittest
{
- ubyte* p;
- for (p = this.rep; p < this.rep + this.size; ++p)
- {
- ++*p;
- if (*p > 0)
- {
- return;
- }
- }
- if (p == this.rep + this.size)
- {
- expand();
- }
+ auto integer = Integer(4294967295);
+ integer <<= 1;
+ assert(integer == 8589934590);
}
/**
@@ -954,15 +916,15 @@ struct Integer
*/
Integer opUnary(string op : "~")() const
{
- auto ret = Integer(this, this.allocator);
- ret.rep[0 .. ret.size].each!((ref a) => a = ~a);
+ auto ret = Integer(this, allocator);
+ ret.rep[0 .. ret.size].each!((ref a) => a = ~a & mask);
return ret;
}
/// Ditto.
Integer opUnary(string op : "-")() const
{
- auto ret = Integer(this, this.allocator);
+ auto ret = Integer(this, allocator);
ret.sign = ret.sign ? Sign.positive : Sign.negative;
return ret;
}
@@ -980,35 +942,24 @@ struct Integer
return this;
}
- private @nogc unittest
+ //
+ nothrow @safe @nogc unittest
{
auto h1 = Integer(79);
Integer h2;
h2 = +h1;
- assert(h2.length == 1);
- assert(h2.rep[0] == 79);
- assert(!h2.sign);
- assert(h2 !is h1);
+ assert(h2 == 79);
h2 = -h1;
- assert(h2.length == 1);
- assert(h2.rep[0] == 79);
- assert(h2.sign);
-
- h1 = -h2;
- assert(h2.length == 1);
- assert(h2.rep[0] == 79);
- assert(h2.sign);
- assert(h2 !is h1);
+ assert(h2 == -79);
+ assert(h1 == 79);
h1 = -h2;
- assert(h1.length == 1);
- assert(h1.rep[0] == 79);
- assert(!h1.sign);
+ assert(h1 == 79);
h2 = ~h1;
- assert(h2.rep[0] == ~cast(ubyte) 79);
+ assert(h2 == ~cast(ubyte) 79);
}
/// Ditto.
@@ -1016,15 +967,11 @@ struct Integer
{
if (this.sign)
{
- decrement();
- if (this.length == 0)
- {
- this.sign = Sign.positive;
- }
+ subtract(1, this);
}
else
{
- increment();
+ add(1, this);
}
return this;
}
@@ -1032,59 +979,56 @@ struct Integer
/// Ditto.
ref Integer opUnary(string op : "--")()
{
- if (this.sign)
+ if (this.size == 0)
{
- increment();
+ add(1, this);
+ this.sign = Sign.negative;
+ }
+ else if (this.sign)
+ {
+ add(1, this);
}
else
{
- decrement();
+ subtract(1, this);
}
return this;
}
- private @nogc unittest
+ ///
+ nothrow @safe @nogc unittest
{
Integer integer;
++integer;
- assert(integer.length == 1);
- assert(integer.rep[0] == 0x01);
+ assert(integer == 1);
--integer;
- assert(integer.length == 0);
+ assert(integer == 0);
integer = 511;
++integer;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0x02 && integer.rep[0] == 0x00);
+ assert(integer == 512);
--integer;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0x01 && integer.rep[0] == 0xff);
+ assert(integer == 511);
integer = 79;
++integer;
- assert(integer.length == 1);
- assert(integer.rep[0] == 0x50);
+ assert(integer == 80);
--integer;
- assert(integer.length == 1);
- assert(integer.rep[0] == 0x4f);
+ assert(integer == 79);
- integer = 65535;
+ integer = -2;
++integer;
- assert(integer.length == 3);
- assert(integer.rep[2] == 0x01 && integer.rep[1] == 0x00 && integer.rep[0] == 0x00);
+ assert(integer == -1);
- --integer;
- assert(integer.length == 2);
- assert(integer.rep[1] == 0xff && integer.rep[0] == 0xff);
-
- integer = -2;
++integer;
- assert(integer.length == 1);
- assert(integer.rep[0] == 0x01);
+ assert(integer == 0);
+
+ --integer;
+ assert(integer == -1);
}
/**
@@ -1093,13 +1037,13 @@ struct Integer
* Params:
* op = Operation.
* operand = The second operand.
+ *
+ * Returns: Result.
*/
- Integer opBinary(string op)(const auto ref Integer operand) const
- if (op == "+" || op == "-" || op == "*" || op == "^^")
+ Integer opBinary(string op)(auto ref const Integer operand) const
+ if ((op == "+" || op == "-") || (op == "*"))
{
- auto ret = Integer(this, this.allocator);
- mixin("ret " ~ op ~ "= operand;");
- return ret;
+ mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// Ditto.
@@ -1111,84 +1055,406 @@ struct Integer
}
body
{
- auto ret = Integer(this, this.allocator);
- mixin("ret " ~ op ~ "= operand;");
- return ret;
+ mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
/// Ditto.
- Integer opBinary(string op)(const auto ref size_t operand) const
+ Integer opBinary(string op)(const size_t operand) const
if (op == "<<" || op == ">>")
{
- auto ret = Integer(this, this.allocator);
- mixin("ret " ~ op ~ "= operand;");
- return ret;
+ mixin("return Integer(this, allocator) " ~ op ~ "= operand;");
}
- ///
- @safe @nogc unittest
+ // Shift right a certain amount of digits.
+ private void shiftRight(const size_t operand) nothrow @safe @nogc
{
- auto integer1 = Integer(425);
- auto integer2 = integer1 << 1;
- assert(integer2 == 850);
+ if (operand == 0)
+ {
+ return;
+ }
+ if (this.size <= operand)
+ {
+ this = 0;
+ return;
+ }
+ const reducedSize = this.size - operand;
- integer2 = integer1 >> 1;
- assert(integer2 == 212);
+ this.rep[operand .. this.size].copy(this.rep[0 .. reducedSize]);
+ this.rep[reducedSize .. this.size].fill(digit.init);
+ this.size = reducedSize;
}
- /**
- * Casting.
- *
- * Params:
- * T = Target type.
- *
- * Returns: $(D_KEYWORD false) if the $(D_PSYMBOL Integer) is 0,
- * $(D_KEYWORD true) otherwise.
- */
- T opCast(T : bool)() const
+ // Shift left a certain amount of digits.
+ private void shiftLeft(const size_t operand) nothrow @safe @nogc
{
- return this.length > 0;
+ 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);
}
- /// Ditto.
- T opCast(T)() const
- if (isIntegral!T && isSigned!T)
+ private void multiply(const digit factor, ref Integer product)
+ const nothrow @safe @nogc
{
- return this.sign ? -(cast(Unsigned!T) this) : cast(Unsigned!T) this;
+ 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();
}
- /// Ditto.
- T opCast(T)() const @trusted
- if (isIntegral!T && isUnsigned!T)
+ private void multiply(ref const Integer factor,
+ ref Integer product,
+ const size_t digits) const nothrow @safe @nogc
{
- if (this.length == 0)
+ Integer intermediate;
+ intermediate.grow(digits);
+ intermediate.size = digits;
+
+ for (size_t i; i < this.size; ++i)
{
- return 0;
+ 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;
+ }
}
- T ret;
- const(ubyte)* src = this.rep;
- ubyte shift;
- for (; src < this.rep + this.size && shift <= T.sizeof * 8; ++src, shift += 8)
+ intermediate.contract();
+ swap(product, intermediate);
+ }
+
+ private void divide(Q, ARGS...)(ref const Integer divisor,
+ auto ref Q quotient,
+ ref ARGS args)
+ const nothrow @safe @nogc
+ 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.");
+ }
+ body
+ {
+ if (compare(divisor) < 0)
{
- ret |= (cast(T) *src) << shift;
+ 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]);
}
- return ret;
}
- ///
- @safe @nogc unittest
+ private Integer square() nothrow @safe @nogc
{
- auto integer = Integer(79);
- assert(cast(long) integer == 79);
+ Integer result;
+ const resultSize = 2 * this.size + 1;
- integer = -79;
- assert(cast(long) integer == -79);
+ result.grow(resultSize);
+ result.size = resultSize;
- integer = 4294967295;
- assert(cast(long) integer == 4294967295);
+ 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]);
- integer = -4294967295;
- assert(cast(long) integer == -4294967295);
+ 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;
+ }
+
+ Vector!ubyte toVector() const nothrow @safe @nogc
+ {
+ Vector!ubyte vector;
+ bool firstBit;
+ ubyte carry;
+
+ if (this.size == 0)
+ {
+ return vector;
+ }
+ vector.reserve(this.size * digit.sizeof);
+
+ // The first digit needs extra handling since it can have leading
+ // non significant zeros.
+ int digitCount = digitBitCount - 8;
+ const first = this.rep[this.size - 1];
+ const prevBitCount = ((this.size - 1) * digitBitCount);
+ const fullBytesBitCount = ((prevBitCount - 1) / 8 + 1) * 8;
+
+ // Find out the right alignment of the first byte.
+ if ((fullBytesBitCount - prevBitCount) == 0)
+ {
+ digitCount -= digit.sizeof * 8 - digitBitCount;
+ }
+ for (; digitCount >= 0; digitCount -= 8)
+ {
+ if (firstBit || ((first >> digitCount) != 0))
+ {
+ firstBit = true;
+ vector.insertBack(cast(ubyte) (first >> digitCount));
+ }
+ }
+ if (digitCount >= -8)
+ {
+ carry = (first << -digitCount) & 0xff;
+ digitCount += digitBitCount;
+ }
+ else
+ {
+ carry = 0;
+ digitCount = digitBitCount - 8;
+ }
+
+ foreach_reverse (d; this.rep[0 .. this.size - 1])
+ {
+ if (carry != 0) // Check the carry from the previous digit.
+ {
+ vector.insertBack(cast(ubyte) (carry | (d >> digitCount)));
+ digitCount -= 8;
+ }
+ // Write the digit by bytes.
+ for (; digitCount >= 0; digitCount -= 8)
+ {
+ vector.insertBack(cast(ubyte) (d >> digitCount));
+ }
+ // Check for an incomplete byte.
+ if (digitCount >= -8)
+ {
+ carry = (d << -digitCount) & 0xff;
+ digitCount += digitBitCount;
+ }
+ else
+ {
+ carry = 0;
+ digitCount = digitBitCount - 8;
+ }
+ }
+ if (carry != 0)
+ {
+ vector.insertBack(cast(ubyte) (carry >> (digitBitCount - digitCount)));
+ }
+
+ return vector;
+ }
+
+ ///
+ nothrow @safe @nogc unittest
+ {
+ {
+ auto integer = Integer(0x66778899aabbddee);
+ ubyte[8] expected = [ 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xdd, 0xee ];
+
+ auto vector = integer.toVector();
+ assert(equal(vector[], expected[]));
+ }
+ {
+ auto integer = Integer(0x03);
+ ubyte[1] expected = [ 0x03 ];
+
+ auto vector = integer.toVector();
+ assert(equal(vector[], 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 vector = integer.toVector();
+ assert(equal(vector[], 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 vector = integer.toVector();
+ assert(equal(vector[], expected[]));
+ }
}
mixin DefaultAllocator;
diff --git a/source/tanya/math/package.d b/source/tanya/math/package.d
index fd5f2a2..3537bc3 100644
--- a/source/tanya/math/package.d
+++ b/source/tanya/math/package.d
@@ -90,18 +90,19 @@ body
size_t i;
auto tmp1 = Integer(x, x.allocator);
auto result = Integer(x.allocator);
+ bool firstBit;
- if (x.length == 0 && y.length != 0)
+ if (x.size == 0 && y.size != 0)
{
- i = y.length;
+ i = y.size;
}
else
{
result = 1;
}
- while (i < y.length)
+ while (i < y.size)
{
- for (ubyte mask = 0x01; mask; mask <<= 1)
+ for (uint mask = 0x01; mask != 0x10000000; mask <<= 1)
{
if (y.rep[i] & mask)
{