diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 76356a4..14b50fd 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -11,7 +11,7 @@ * All algorithms in this module are lazy, they request the next element of the * original range on demand. * - * Copyright: Eugene Wissner 2018-2020. + * Copyright: Eugene Wissner 2018-2021. * 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) @@ -20,554 +20,12 @@ */ module tanya.algorithm.iteration; -import std.algorithm.comparison; import std.typecons; import tanya.memory.lifetime; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; -// These predicates are used to help preserve `const` and `inout` for -// ranges built on other ranges. - -private enum hasInoutFront(T) = is(typeof((inout ref T a) => a.front)); -private enum hasInoutBack(T) = is(typeof((inout ref T a) => a.back)); -private enum hasInoutIndex(T) = is(typeof((inout ref T a, size_t i) => a[i])); - -private enum hasConstEmpty(T) = is(typeof(((const T* a) => (*a).empty)(null)) : bool); -private enum hasConstLength(T) = is(typeof(((const T* a) => (*a).length)(null)) : size_t); -private enum hasConstSave(T) = is(typeof(((const T* a) => (*a).save())(null)) : T); -private enum hasConstSlice(T) = is(typeof(((const T* a) => (*a)[0 .. $])(null)) : T); - -@nogc nothrow pure @safe unittest -{ - // Test the definitions. - static assert(hasInoutFront!string); - static assert(hasInoutBack!string); - static assert(hasInoutIndex!string); - static assert(hasConstEmpty!string); - static assert(hasConstLength!string); - static assert(hasConstSave!string); - static assert(hasConstSlice!string); - - // Test that Take propagates const/inout correctly. - alias TakeString = Take!(string, false); - static assert(hasInoutFront!TakeString); - static assert(hasInoutBack!TakeString); - static assert(hasInoutIndex!TakeString); - static assert(hasConstEmpty!TakeString); - static assert(hasConstLength!TakeString); - static assert(hasConstSave!TakeString); - static assert(hasConstSlice!TakeString); - - // Test that Retro propagates const/inout correctly. - alias RetroString = Retro!string; - static assert(hasInoutFront!RetroString); - static assert(hasInoutBack!RetroString); - static assert(hasInoutIndex!RetroString); - static assert(hasConstEmpty!RetroString); - static assert(hasConstLength!RetroString); - static assert(hasConstSave!RetroString); - static assert(hasConstSlice!RetroString); -} - -private struct Take(R, bool exactly) -{ - private R source; - size_t length_; - - @disable this(); - - private this(R source, size_t length) - { - this.source = source; - static if (!exactly && hasLength!R) - { - this.length_ = min(source.length, length); - } - else - { - this.length_ = length; - } - } - - mixin(`@property auto ref front() ` ~ (hasInoutFront!R ? `inout ` : ``) ~ - `in (!empty) - { - return this.source.front; - }`); - - void popFront() - in (!empty) - { - this.source.popFront(); - --this.length_; - } - - mixin(`@property bool empty() ` ~ (exactly || isInfinite!R || hasConstEmpty!R ? `const ` : ``) ~ - `{ - static if (exactly || isInfinite!R) - { - return length == 0; - } - else - { - return this.length_ == 0 || this.source.empty; - } - }`); - - static if (exactly || hasLength!R) - { - @property size_t length() const - { - return this.length_; - } - } - - static if (hasAssignableElements!R) - { - @property void front(ref ElementType!R value) - in (!empty) - { - this.source.front = value; - } - - @property void front(ElementType!R value) - in (!empty) - { - this.source.front = move(value); - } - } - - static if (isForwardRange!R) - { - mixin(`typeof(this) save() ` ~ (hasConstSave!R ? `const ` : ``) ~ - `{ - return typeof(this)(this.source.save(), length); - }`); - } - static if (isRandomAccessRange!R) - { - mixin(`@property auto ref back() ` ~ (hasInoutBack!R ? `inout ` : ``) ~ - `in (!empty) - { - return this.source[this.length - 1]; - }`); - - void popBack() - in (!empty) - { - --this.length_; - } - - mixin(`auto ref opIndex(size_t i) ` ~ (hasInoutIndex!R ? `inout ` : ``) ~ - `in (i < length) - { - return this.source[i]; - }`); - - static if (hasAssignableElements!R) - { - @property void back(ref ElementType!R value) - in (!empty) - { - this.source[length - 1] = value; - } - - @property void back(ElementType!R value) - in (!empty) - { - this.source[length - 1] = move(value); - } - - void opIndexAssign(ref ElementType!R value, size_t i) - in (i < length) - { - this.source[i] = value; - } - - void opIndexAssign(ElementType!R value, size_t i) - in (i < length) - { - this.source[i] = move(value); - } - } - } - - static if (!exactly && hasSlicing!R) - { - static if (is(typeof(length))) alias opDollar = length; - - mixin(`auto opSlice(size_t i, size_t j) ` ~ (hasConstSlice!R ? `const ` : ``) ~ - `in (i <= j) - in (j <= length) - { - return typeof(this)(this.source[i .. j], length); - }`); - } - - version (unittest) static assert(isInputRange!Take); -} - -/** - * Takes $(D_PARAM n) elements from $(D_PARAM range). - * - * If $(D_PARAM range) doesn't have $(D_PARAM n) elements, the resulting range - * spans all elements of $(D_PARAM range). - * - * $(D_PSYMBOL take) is particulary useful with infinite ranges. You can take - ` $(B n) elements from such range and pass the result to an algorithm which - * expects a finit range. - * - * Params: - * R = Type of the adapted range. - * range = The range to take the elements from. - * n = The number of elements to take. - * - * Returns: A range containing maximum $(D_PARAM n) first elements of - * $(D_PARAM range). - * - * See_Also: $(D_PSYMBOL takeExactly). - */ -auto take(R)(R range, size_t n) -if (isInputRange!R) -{ - static if (hasSlicing!R && hasLength!R) - { - if (range.length <= n) - return range; - else - return range[0 .. n]; - } - // Special case: take(take(...), n) - else static if (is(Range == Take!(RRange, exact), RRange, bool exact)) - { - if (n > range.length_) - n = range.length_; - static if (exact) - // `take(takeExactly(r, n0), n)` is rewritten `takeExactly(r, min(n0, n))`. - return Take!(RRange, true)(range.source, n); - else - // `take(take(r, n0), n)` is rewritten `take(r, min(n0, n))`. - return Take!(RRange, false)(range.source, n); - } - else static if (isInfinite!R) - { - // If the range is infinite then `take` is the same as `takeExactly`. - return Take!(R, true)(range, n); - } - else - { - return Take!(R, false)(range, n); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct InfiniteRange - { - private size_t front_ = 1; - - enum bool empty = false; - - @property size_t front() @nogc nothrow pure @safe - { - return this.front_; - } - - @property void front(size_t i) @nogc nothrow pure @safe - { - this.front_ = i; - } - - void popFront() @nogc nothrow pure @safe - { - ++this.front_; - } - - size_t opIndex(size_t i) @nogc nothrow pure @safe - { - return this.front_ + i; - } - - void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe - { - this.front = i + value; - } - - InfiniteRange save() @nogc nothrow pure @safe - { - return this; - } - } - - auto t = InfiniteRange().take(3); - assert(t.length == 3); - assert(t.front == 1); - assert(t.back == 3); - - t.popFront(); - assert(t.front == 2); - assert(t.back == 3); - - t.popBack(); - assert(t.front == 2); - assert(t.back == 2); - - t.popFront(); - assert(t.empty); -} - -/** - * Takes exactly $(D_PARAM n) elements from $(D_PARAM range). - * - * $(D_PARAM range) must have at least $(D_PARAM n) elements. - * - * $(D_PSYMBOL takeExactly) is particulary useful with infinite ranges. You can - ` take $(B n) elements from such range and pass the result to an algorithm - * which expects a finit range. - * - * Params: - * R = Type of the adapted range. - * range = The range to take the elements from. - * n = The number of elements to take. - * - * Returns: A range containing $(D_PARAM n) first elements of $(D_PARAM range). - * - * See_Also: $(D_PSYMBOL take). - */ -auto takeExactly(R)(R range, size_t n) -if (isInputRange!R) -{ - static if (hasSlicing!R) - { - return range[0 .. n]; - } - // Special case: takeExactly(take(range, ...), n) is takeExactly(range, n) - else static if (is(Range == Take!(RRange, exact), RRange, bool exact)) - { - assert(n <= range.length_); - return Take!(RRange, true)(range.source, n); - } - else - { - return Take!(R, true)(range, n); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static struct InfiniteRange - { - private size_t front_ = 1; - - enum bool empty = false; - - @property size_t front() @nogc nothrow pure @safe - { - return this.front_; - } - - @property void front(size_t i) @nogc nothrow pure @safe - { - this.front_ = i; - } - - void popFront() @nogc nothrow pure @safe - { - ++this.front_; - } - - size_t opIndex(size_t i) @nogc nothrow pure @safe - { - return this.front_ + i; - } - - void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe - { - this.front = i + value; - } - - InfiniteRange save() @nogc nothrow pure @safe - { - return this; - } - } - - auto t = InfiniteRange().takeExactly(3); - assert(t.length == 3); - assert(t.front == 1); - assert(t.back == 3); - - t.popFront(); - assert(t.front == 2); - assert(t.back == 3); - - t.popBack(); - assert(t.front == 2); - assert(t.back == 2); - - t.popFront(); - assert(t.empty); -} - -// Reverse-access-order range returned by `retro`. -private struct Retro(Range) -{ - Range source; - - @disable this(); - - private this(Range source) @safe - { - this.source = source; - } - - mixin(`Retro save() ` ~ (hasConstSave!Range ? `const ` : ``) ~ - `{ - return Retro(source.save()); - }`); - - mixin(`@property auto ref front() ` ~ (hasInoutBack!Range ? `inout ` : ``) ~ - `in (!empty) - { - return this.source.back; - }`); - - void popFront() - in (!empty) - { - this.source.popBack(); - } - - mixin(`@property auto ref back() ` ~ (hasInoutFront!Range ? `inout ` : ``) ~ - `in (!empty) - { - return this.source.front; - }`); - - void popBack() - in (!empty) - { - this.source.popFront(); - } - - mixin(`@property bool empty() ` ~ (hasConstEmpty!Range ? `const ` : ``) ~ - `{ - return this.source.empty; - }`); - - static if (hasLength!Range) - { - mixin(`@property size_t length() ` ~ (hasConstLength!Range ? `const ` : ``) ~ - `{ - return this.source.length; - }`); - } - - static if (isRandomAccessRange!Range && hasLength!Range) - { - mixin(`auto ref opIndex(size_t i) ` ~ (hasInoutIndex!Range ? `inout ` : ``) ~ - `in (i < length) - { - return this.source[$ - ++i]; - }`); - } - - static if (hasLength!Range && hasSlicing!Range) - { - alias opDollar = length; - - mixin(`auto opSlice(size_t i, size_t j) ` ~ (hasConstSlice!Range ? `const ` : ``) ~ - `in (i <= j) - in (j <= length) - { - return typeof(this)(this.source[$-j .. $-i]); - }`); - } - - static if (hasAssignableElements!Range) - { - @property void front(ref ElementType!Range value) - in (!empty) - { - this.source.back = value; - } - - @property void front(ElementType!Range value) - in (!empty) - { - this.source.back = move(value); - } - - @property void back(ref ElementType!Range value) - in (!empty) - { - this.source.front = value; - } - - @property void back(ElementType!Range value) - in (!empty) - { - this.source.front = move(value); - } - - static if (isRandomAccessRange!Range && hasLength!Range) - { - void opIndexAssign(ref ElementType!Range value, size_t i) - in (i < length) - { - this.source[$ - ++i] = value; - } - - void opIndexAssign(ElementType!Range value, size_t i) - in (i < length) - { - this.source[$ - ++i] = move(value); - } - } - } - - version (unittest) static assert(isBidirectionalRange!Retro); -} - -/** - * Iterates a bidirectional range backwards. - * - * If $(D_PARAM Range) is a random-access range as well, the resulting range - * is a random-access range too. - * - * Params: - * Range = Bidirectional range type. - * range = Bidirectional range. - * - * Returns: Bidirectional range with the elements order reversed. - */ -auto retro(Range)(Range range) -if (isBidirectionalRange!Range) -{ - // Special case: retro(retro(range)) is range - static if (is(Range == Retro!RRange, RRange)) - return range.source; - else - return Retro!Range(range); -} - -/// -@nogc nothrow pure @safe unittest -{ - const int[3] given = [1, 2, 3]; - const int[3] expected = [3, 2, 1]; - - auto actual = retro(given[]); - - assert(actual.length == expected.length); - assert(!actual.empty); - assert(equal(actual, expected[])); -} - private struct SingletonByValue(E) { private Nullable!E element; @@ -719,61 +177,9 @@ auto singleton(E)(return ref E element) /** * Accumulates all elements of a range using a function. * - * $(D_PSYMBOL foldl) takes a function, an input range and the initial value. - * The function takes this initial value and the first element of the range (in - * this order), puts them together and returns the result. The return - * type of the function should be the same as the type of the initial value. - * This is than repeated for all the remaining elements of the range, whereby - * the value returned by the passed function is used at the place of the - * initial value. - * - * $(D_PSYMBOL foldl) accumulates from left to right. - * - * Params: - * F = Callable accepting the accumulator and a range element. - */ -template foldl(F...) -if (F.length == 1) -{ - /** - * Params: - * R = Input range type. - * T = Type of the accumulated value. - * range = Input range. - * init = Initial value. - * - * Returns: Accumulated value. - */ - auto foldl(R, T)(R range, auto ref T init) - if (isInputRange!R && !isInfinite!R) - { - if (range.empty) - { - return init; - } - else - { - auto acc = F[0](init, getAndPopFront(range)); - return foldl(range, acc); - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[3] range = [1, 2, 3]; - const actual = foldl!((acc, x) => acc + x)(range[], 0); - - assert(actual == 6); -} - -/** - * Accumulates all elements of a range using a function. - * - * $(D_PSYMBOL foldr) takes a function, an input range and the initial value. - * The function takes this initial value and the first element of the range (in - * this order), puts them together and returns the result. The return + * $(D_PSYMBOL foldr) takes a function, a bidirectional range and the initial + * value. The function takes this initial value and the first element of the + * range (in this order), puts them together and returns the result. The return * type of the function should be the same as the type of the initial value. * This is than repeated for all the remaining elements of the range, whereby * the value returned by the passed function is used at the place of the diff --git a/source/tanya/algorithm/package.d b/source/tanya/algorithm/package.d index 1aee863..90e9a8a 100644 --- a/source/tanya/algorithm/package.d +++ b/source/tanya/algorithm/package.d @@ -5,7 +5,7 @@ /** * Collection of generic algorithms. * - * Copyright: Eugene Wissner 2017-2020. + * Copyright: Eugene Wissner 2017-2021. * 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) @@ -16,4 +16,3 @@ module tanya.algorithm; public import tanya.algorithm.iteration; public import tanya.algorithm.mutation; -public import tanya.algorithm.searching; diff --git a/source/tanya/algorithm/searching.d b/source/tanya/algorithm/searching.d deleted file mode 100644 index 63e870f..0000000 --- a/source/tanya/algorithm/searching.d +++ /dev/null @@ -1,53 +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/. */ - -/** - * Searching algorithms. - * - * Copyright: Eugene Wissner 2018-2020. - * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, - * Mozilla Public License, v. 2.0). - * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/searching.d, - * tanya/algorithm/searching.d) - */ -module tanya.algorithm.searching; - -import tanya.range; - -/** - * Counts the elements in an input range. - * - * If $(D_PARAM R) has length, $(D_PSYMBOL count) returns it, otherwise it - * iterates over the range and counts the elements. - * - * Params: - * R = Input range type. - * range = Input range. - * - * Returns: $(D_PARAM range) length. - */ -size_t count(R)(R range) -if (isInputRange!R) -{ - static if (hasLength!R) - { - return range.length; - } - else - { - size_t counter; - for (; !range.empty; range.popFront(), ++counter) - { - } - return counter; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - int[3] array; - assert(count(array) == 3); -} diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index dcd2074..d2e0d09 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -5,7 +5,7 @@ /** * Single-dimensioned array. * - * Copyright: Eugene Wissner 2016-2020. + * Copyright: Eugene Wissner 2016-2021. * 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) @@ -16,7 +16,7 @@ module tanya.container.array; import core.checkedint; import std.algorithm.comparison; -import tanya.algorithm.iteration; +import std.algorithm.iteration; import tanya.algorithm.mutation; import tanya.memory.allocator; import tanya.memory.lifetime; @@ -676,7 +676,7 @@ struct Array(T) { reserve(length + el.length); } - return foldl!((acc, e) => acc + insertBack(e))(el, 0U); + return fold!((acc, e) => acc + insertBack(e))(el, size_t.init); } /// ditto diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d index 15bb82d..4dc80c8 100644 --- a/source/tanya/container/hashtable.d +++ b/source/tanya/container/hashtable.d @@ -5,7 +5,7 @@ /** * Hash table. * - * Copyright: Eugene Wissner 2018-2020. + * Copyright: Eugene Wissner 2018-2021. * 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) @@ -14,7 +14,7 @@ */ module tanya.container.hashtable; -import tanya.algorithm.iteration; +import std.algorithm.iteration; import tanya.algorithm.mutation; import tanya.container.array; import tanya.container.entry; @@ -719,7 +719,7 @@ if (isHashFunction!(hasher, Key)) size_t insert(R)(scope R range) if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R) { - return foldl!((acc, x) => acc + insert(x))(range, 0U); + return fold!((acc, x) => acc + insert(x))(range, size_t.init); } /// diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 0d8fc3d..56c8d22 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -6,7 +6,7 @@ * This module contains singly-linked ($(D_PSYMBOL SList)) and doubly-linked * ($(D_PSYMBOL DList)) lists. * - * Copyright: Eugene Wissner 2016-2020. + * Copyright: Eugene Wissner 2016-2021. * 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) @@ -16,7 +16,7 @@ module tanya.container.list; import std.algorithm.comparison; -import tanya.algorithm.iteration; +import std.algorithm.iteration; import tanya.container.entry; import tanya.memory.allocator; import tanya.memory.lifetime; @@ -1693,7 +1693,7 @@ struct DList(T) && isImplicitlyConvertible!(ElementType!R, T)) in (checkRangeBelonging(r)) { - return foldl!((acc, x) => acc + insertAfter(r, x))(el, 0U); + return fold!((acc, x) => acc + insertAfter(r, x))(el, size_t.init); } /// diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index 6fad9bb..bd8f817 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -1342,7 +1342,7 @@ struct String /// @nogc pure @safe unittest { - import tanya.algorithm.searching : count; + import std.algorithm.searching : count; auto s = String("Из пословицы слова не выкинешь."); diff --git a/tests/tanya/algorithm/tests/iteration.d b/tests/tanya/algorithm/tests/iteration.d index 52e99b3..9594d5e 100644 --- a/tests/tanya/algorithm/tests/iteration.d +++ b/tests/tanya/algorithm/tests/iteration.d @@ -1,85 +1,13 @@ /* 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/. */ + module tanya.algorithm.tests.iteration; import tanya.algorithm.iteration; import tanya.range; import tanya.test.stub; -// length is unknown when taking from a range without length -@nogc nothrow pure @safe unittest -{ - static struct R - { - mixin InputRangeStub; - } - auto actual = take(R(), 100); - - static assert(!hasLength!(typeof(actual))); -} - -// Takes minimum length if the range length > n -@nogc nothrow pure @safe unittest -{ - auto range = take(cast(int[]) null, 8); - assert(range.length == 0); -} - -@nogc nothrow pure @safe unittest -{ - const int[9] range = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - { - auto slice = take(range[], 8)[1 .. 3]; - - assert(slice.length == 2); - assert(slice.front == 2); - assert(slice.back == 3); - } - { - auto slice = takeExactly(range[], 8)[1 .. 3]; - - assert(slice.length == 2); - assert(slice.front == 2); - assert(slice.back == 3); - } -} - -// Elements are accessible in reverse order -@nogc nothrow pure @safe unittest -{ - const int[3] given = [1, 2, 3]; - auto actual = retro(given[]); - - assert(actual.back == given[].front); - assert(actual[0] == 3); - assert(actual[2] == 1); - - actual.popBack(); - assert(actual.back == 2); - assert(actual[1] == 2); - - // Check slicing. - auto slice = retro(given[])[1 .. $]; - assert(slice.length == 2 && slice.front == 2 && slice.back == 1); -} - -// Elements can be assigned -@nogc nothrow pure @safe unittest -{ - int[4] given = [1, 2, 3, 4]; - auto actual = retro(given[]); - - actual.front = 5; - assert(given[].back == 5); - - actual.back = 8; - assert(given[].front == 8); - - actual[2] = 10; - assert(given[1] == 10); -} - // Singleton range is bidirectional and random-access @nogc nothrow pure @safe unittest { diff --git a/tests/tanya/algorithm/tests/searching.d b/tests/tanya/algorithm/tests/searching.d deleted file mode 100644 index ca8fdb0..0000000 --- a/tests/tanya/algorithm/tests/searching.d +++ /dev/null @@ -1,17 +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/. */ -module tanya.algorithm.tests.searching; - -import tanya.algorithm.searching; -import tanya.test.stub; - -@nogc nothrow pure @safe unittest -{ - @Count(3) - static struct Range - { - mixin InputRangeStub!int; - } - assert(count(Range()) == 3); -}