From 9e6f5c3105c05bf6e5ff33dbd2b3d2015cdb9e53 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 12 Nov 2018 07:54:52 +0100 Subject: [PATCH] Add algorithm.mutation.rotate --- source/tanya/algorithm/mutation.d | 86 +++++++++++++++++++++++++++++++ source/tanya/container/array.d | 12 ++--- source/tanya/container/string.d | 8 ++- 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index 99ba239..9c05826 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -607,3 +607,89 @@ if (isInputRange!Range && hasLvalueElements!Range) assert(counter == 2); } + +/** + * Rotates the elements of a union of two ranges. + * + * Performs a left rotation on the given ranges, as if it would be a signle + * range, so that [`front.front`, `back.front`$(RPAREN) is a valid range, that + * is $(D_PARAM back) would continue $(D_PARAM front). + * + * The elements are moved so, that the first element of $(D_PARAM back) becomes + * the first element of $(D_PARAM front) without changing the relative order of + * their elements. + * + * Params: + * Range = Range type. + * front = Left half. + * back = Right half. + */ +void rotate(Range)(Range front, Range back) +if (isForwardRange!Range && hasSwappableElements!Range) +{ + auto next = back.save(); + + while (!front.empty && !next.empty && !sameHead(front, next)) + { + swap(front.front, next.front); + front.popFront(); + next.popFront(); + + if (next.empty) + { + next = back.save(); + } + else if (front.empty) + { + front = back.save(); + back = next.save(); + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + import tanya.algorithm.comparison : equal; + + const int[7] expected = [1, 2, 3, 4, 5, 6, 7]; + int[7] actual = [5, 6, 3, 4, 1, 2, 7]; + + rotate(actual[0 .. 2], actual[4 .. 6]); + assert(equal(actual[], expected[])); +} + +@nogc nothrow pure @safe unittest +{ + import tanya.algorithm.comparison : equal; + + const int[5] expected = [1, 2, 3, 4, 5]; + int[5] actual = [4, 5, 1, 2, 3]; + + rotate(actual[0 .. 2], actual[2 .. $]); + assert(equal(actual[], expected[])); +} + +// Doesn't cause an infinite loop if back is shorter than the front +@nogc nothrow pure @safe unittest +{ + import tanya.algorithm.comparison : equal; + + const int[5] expected = [1, 2, 3, 4, 5]; + int[5] actual = [3, 4, 5, 1, 2]; + + rotate(actual[0 .. 3], actual[3 .. $]); + assert(equal(actual[], expected[])); +} + +// Doesn't call .front on an empty front +@nogc nothrow pure @safe unittest +{ + import tanya.algorithm.comparison : equal; + + const int[2] expected = [2, 8]; + int[2] actual = expected; + + rotate(actual[0 .. 0], actual[]); + assert(equal(actual[], expected[])); +} diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index e0556dd..c2e35e2 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -15,7 +15,6 @@ module tanya.container.array; import core.checkedint; -import std.algorithm.mutation : bringToFront; import tanya.algorithm.comparison; import tanya.algorithm.mutation; import tanya.exception; @@ -804,10 +803,11 @@ struct Array(T) } do { - const oldLen = length; - const offset = r.end - this.data; + const oldLength = length; + const after = r.end - this.data; const inserted = insertBack(el); - bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); + + rotate(this.data[after .. oldLength], this.data[oldLength .. length]); return inserted; } @@ -846,7 +846,7 @@ struct Array(T) { moveBack(el); } - bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); + rotate(this.data[offset .. oldLen], this.data[oldLen .. length]); return 1; } @@ -902,7 +902,7 @@ struct Array(T) { moveBack(el); } - bringToFront(this.data[offset .. oldLen], this.data[oldLen .. length]); + rotate(this.data[offset .. oldLen], this.data[oldLen .. length]); return 1; } diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index e8c2d71..abe60e1 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -26,7 +26,6 @@ */ module tanya.container.string; -import std.algorithm.mutation : bringToFront; import tanya.algorithm.comparison; import tanya.algorithm.mutation; import tanya.hash.lookup; @@ -1531,11 +1530,10 @@ struct String do { const oldLength = length; - const rangeEnd = r.end - this.data; + const after = r.end - this.data; const inserted = insertBack(el); - auto containerEnd = this.data + oldLength; - bringToFront(ByCodeUnit!char(this, this.data + rangeEnd, containerEnd), - ByCodeUnit!char(this, containerEnd, this.data + length)); + + rotate(this.data[after .. oldLength], this.data[oldLength .. length]); return inserted; }