// Written in the D programming language. /** This package implements generic algorithms oriented towards the processing of sequences. Sequences processed by these functions define range-based interfaces. See also $(LINK2 std_range.html, Reference on ranges) and $(WEB ddili.org/ders/d.en/ranges.html, tutorial on ranges). Algorithms are categorized into the following submodules: $(BOOKTABLE , $(TR $(TH Category) $(TH Submodule) $(TH Functions) ) $(TR $(TDNW Searching) $(TDNW $(SUBMODULE searching)) $(TD $(SUBREF searching, all) $(SUBREF searching, any) $(SUBREF searching, balancedParens) $(SUBREF searching, boyerMooreFinder) $(SUBREF searching, canFind) $(SUBREF searching, commonPrefix) $(SUBREF searching, count) $(SUBREF searching, countUntil) $(SUBREF searching, endsWith) $(SUBREF searching, find) $(SUBREF searching, findAdjacent) $(SUBREF searching, findAmong) $(SUBREF searching, findSkip) $(SUBREF searching, findSplit) $(SUBREF searching, findSplitAfter) $(SUBREF searching, findSplitBefore) $(SUBREF searching, minCount) $(SUBREF searching, minPos) $(SUBREF searching, skipOver) $(SUBREF searching, startsWith) $(SUBREF searching, until) ) ) $(TR $(TDNW Comparison) $(TDNW $(SUBMODULE comparison)) $(TD $(SUBREF comparison, among) $(SUBREF comparison, castSwitch) $(SUBREF comparison, clamp) $(SUBREF comparison, cmp) $(SUBREF comparison, equal) $(SUBREF comparison, levenshteinDistance) $(SUBREF comparison, levenshteinDistanceAndPath) $(SUBREF comparison, max) $(SUBREF comparison, min) $(SUBREF comparison, mismatch) $(SUBREF comparison, predSwitch) ) ) $(TR $(TDNW Iteration) $(TDNW $(SUBMODULE iteration)) $(TD $(SUBREF iteration, cache) $(SUBREF iteration, cacheBidirectional) $(SUBREF iteration, each) $(SUBREF iteration, filter) $(SUBREF iteration, filterBidirectional) $(SUBREF iteration, group) $(SUBREF iteration, groupBy) $(SUBREF iteration, joiner) $(SUBREF iteration, map) $(SUBREF iteration, reduce) $(SUBREF iteration, splitter) $(SUBREF iteration, sum) $(SUBREF iteration, uniq) ) ) $(TR $(TDNW Sorting) $(TDNW $(SUBMODULE sorting)) $(TD $(SUBREF sorting, completeSort) $(SUBREF sorting, isPartitioned) $(SUBREF sorting, isSorted) $(SUBREF sorting, makeIndex) $(SUBREF sorting, multiSort) $(SUBREF sorting, nextEvenPermutation) $(SUBREF sorting, nextPermutation) $(SUBREF sorting, partialSort) $(SUBREF sorting, partition) $(SUBREF sorting, partition3) $(SUBREF sorting, schwartzSort) $(SUBREF sorting, sort) $(SUBREF sorting, topN) $(SUBREF sorting, topNCopy) ) ) $(TR $(TDNW Set operations) $(TDNW $(SUBMODULE setops)) $(TD $(SUBREF setops, cartesianProduct) $(SUBREF setops, largestPartialIntersection) $(SUBREF setops, largestPartialIntersectionWeighted) $(SUBREF setops, nWayUnion) $(SUBREF setops, setDifference) $(SUBREF setops, setIntersection) $(SUBREF setops, setSymmetricDifference) $(SUBREF setops, setUnion) ) ) $(TR $(TDNW Mutation) $(TDNW $(SUBMODULE mutation)) $(TD $(SUBREF mutation, bringToFront) $(SUBREF mutation, copy) $(SUBREF mutation, fill) $(SUBREF mutation, initializeAll) $(SUBREF mutation, move) $(SUBREF mutation, moveAll) $(SUBREF mutation, moveSome) $(SUBREF mutation, remove) $(SUBREF mutation, reverse) $(SUBREF mutation, strip) $(SUBREF mutation, stripLeft) $(SUBREF mutation, stripRight) $(SUBREF mutation, swap) $(SUBREF mutation, swapRanges) $(SUBREF mutation, uninitializedFill) ) ) $(TR $(TDNW Utility) $(TDNW -) $(TD $(MYREF forward) ) )) Many functions in this package are parameterized with a function or a $(GLOSSARY predicate). The predicate may be passed either as a function name, a delegate name, a $(GLOSSARY functor) name, or a compile-time string. The string may consist of $(B any) legal D expression that uses the symbol $(D a) (for unary functions) or the symbols $(D a) and $(D b) (for binary functions). These names will NOT interfere with other homonym symbols in user code because they are evaluated in a different context. The default for all binary comparison predicates is $(D "a == b") for unordered operations and $(D "a < b") for ordered operations. Example: ---- int[] a = ...; static bool greater(int a, int b) { return a > b; } sort!(greater)(a); // predicate as alias sort!("a > b")(a); // predicate as string // (no ambiguity with array name) sort(a); // no predicate, "a < b" is implicit ---- Macros: WIKI = Phobos/StdAlgorithm SUBMODULE = $(LINK2 std_algorithm_$1.html, std.algorithm.$1) SUBREF = $(LINK2 std_algorithm_$1.html#.$2, $(TT $2))$(NBSP) Copyright: Andrei Alexandrescu 2008-. License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(WEB erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/_algorithm/package.d) */ module std.algorithm; //debug = std_algorithm; public import std.algorithm.comparison; public import std.algorithm.iteration; public import std.algorithm.mutation; public import std.algorithm.setops; public import std.algorithm.searching; public import std.algorithm.sorting; // FIXME import std.functional; // : unaryFun, binaryFun; import std.range.primitives; // FIXME import std.range; // : SortedRange; import std.traits; // FIXME import std.typecons; // : tuple, Tuple; // FIXME import std.typetuple; // : TypeTuple, staticMap, allSatisfy, anySatisfy; version(unittest) debug(std_algorithm) import std.stdio; package T* addressOf(T)(ref T val) { return &val; } // Same as std.string.format, but "self-importing". // Helps reduce code and imports, particularly in static asserts. // Also helps with missing imports errors. package template algoFormat() { import std.format : format; alias algoFormat = format; } /** Forwards function arguments with saving ref-ness. */ template forward(args...) { import std.typetuple; static if (args.length) { alias arg = args[0]; static if (__traits(isRef, arg)) alias fwd = arg; else @property fwd()(){ return move(arg); } alias forward = TypeTuple!(fwd, forward!(args[1..$])); } else alias forward = TypeTuple!(); } /// @safe unittest { class C { static int foo(int n) { return 1; } static int foo(ref int n) { return 2; } } int bar()(auto ref int x) { return C.foo(forward!x); } assert(bar(1) == 1); int i; assert(bar(i) == 2); } /// @safe unittest { void foo(int n, ref string s) { s = null; foreach (i; 0..n) s ~= "Hello"; } // forwards all arguments which are bound to parameter tuple void bar(Args...)(auto ref Args args) { return foo(forward!args); } // forwards all arguments with swapping order void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); } string s; bar(1, s); assert(s == "Hello"); baz(s, 2); assert(s == "HelloHello"); } @safe unittest { auto foo(TL...)(auto ref TL args) { string result = ""; foreach (i, _; args) { //pragma(msg, "[",i,"] ", __traits(isRef, args[i]) ? "L" : "R"); result ~= __traits(isRef, args[i]) ? "L" : "R"; } return result; } string bar(TL...)(auto ref TL args) { return foo(forward!args); } string baz(TL...)(auto ref TL args) { int x; return foo(forward!args[3], forward!args[2], 1, forward!args[1], forward!args[0], x); } struct S {} S makeS(){ return S(); } int n; string s; assert(bar(S(), makeS(), n, s) == "RRLL"); assert(baz(S(), makeS(), n, s) == "LLRRRL"); } @safe unittest { ref int foo(ref int a) { return a; } ref int bar(Args)(auto ref Args args) { return foo(forward!args); } static assert(!__traits(compiles, { auto x1 = bar(3); })); // case of NG int value = 3; auto x2 = bar(value); // case of OK } /** Specifies whether the output of certain algorithm is desired in sorted format. */ enum SortOutput { no, /// Don't sort output yes, /// Sort output } void topNIndex( alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, RangeIndex)(Range r, RangeIndex index, SortOutput sorted = SortOutput.no) if (isIntegral!(ElementType!(RangeIndex))) { import std.container : BinaryHeap; import std.exception : enforce; if (index.empty) return; enforce(ElementType!(RangeIndex).max >= index.length, "Index type too small"); bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b) { return binaryFun!(less)(r[a], r[b]); } auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); foreach (i; 0 .. r.length) { heap.conditionalInsert(cast(ElementType!RangeIndex) i); } if (sorted == SortOutput.yes) { while (!heap.empty) heap.removeFront(); } } void topNIndex( alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, RangeIndex)(Range r, RangeIndex index, SortOutput sorted = SortOutput.no) if (is(ElementType!(RangeIndex) == ElementType!(Range)*)) { import std.container : BinaryHeap; if (index.empty) return; static bool indirectLess(const ElementType!(RangeIndex) a, const ElementType!(RangeIndex) b) { return binaryFun!less(*a, *b); } auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); foreach (i; 0 .. r.length) { heap.conditionalInsert(&r[i]); } if (sorted == SortOutput.yes) { while (!heap.empty) heap.removeFront(); } } unittest { import std.conv : text; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); { int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; int*[] b = new int*[5]; topNIndex!("a > b")(a, b, SortOutput.yes); //foreach (e; b) writeln(*e); assert(b == [ &a[0], &a[2], &a[1], &a[6], &a[5]]); } { int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ]; auto b = new ubyte[5]; topNIndex!("a > b")(a, b, SortOutput.yes); //foreach (e; b) writeln(e, ":", a[e]); assert(b == [ cast(ubyte) 0, cast(ubyte)2, cast(ubyte)1, cast(ubyte)6, cast(ubyte)5], text(b)); } } /+ // topNIndexImpl // @@@BUG1904 /*private*/ void topNIndexImpl( alias less, bool sortAfter, SwapStrategy ss, SRange, TRange)(SRange source, TRange target) { alias lessFun = binaryFun!(less); static assert(ss == SwapStrategy.unstable, "Stable indexing not yet implemented"); alias SIter = Iterator!(SRange); alias TElem = std.iterator.ElementType!(TRange); enum usingInt = isIntegral!(TElem); static if (usingInt) { enforce(source.length <= TElem.max, "Numeric overflow at risk in computing topNIndexImpl"); } // types and functions used within SIter index2iter(TElem a) { static if (!usingInt) return a; else return begin(source) + a; } bool indirectLess(TElem a, TElem b) { return lessFun(*index2iter(a), *index2iter(b)); } void indirectCopy(SIter from, ref TElem to) { static if (!usingInt) to = from; else to = cast(TElem)(from - begin(source)); } // copy beginning of collection into the target auto sb = begin(source), se = end(source), tb = begin(target), te = end(target); for (; sb != se; ++sb, ++tb) { if (tb == te) break; indirectCopy(sb, *tb); } // if the index's size is same as the source size, just quicksort it // otherwise, heap-insert stuff in it. if (sb == se) { // everything in source is now in target... just sort the thing static if (sortAfter) sort!(indirectLess, ss)(target); } else { // heap-insert te = tb; tb = begin(target); target = range(tb, te); makeHeap!(indirectLess)(target); // add stuff to heap for (; sb != se; ++sb) { if (!lessFun(*sb, *index2iter(*tb))) continue; // copy the source over the smallest indirectCopy(sb, *tb); heapify!(indirectLess)(target, tb); } static if (sortAfter) sortHeap!(indirectLess)(target); } } /** topNIndex */ void topNIndex( alias less, SwapStrategy ss = SwapStrategy.unstable, SRange, TRange)(SRange source, TRange target) { return .topNIndexImpl!(less, false, ss)(source, target); } /// Ditto void topNIndex( string less, SwapStrategy ss = SwapStrategy.unstable, SRange, TRange)(SRange source, TRange target) { return .topNIndexImpl!(binaryFun!(less), false, ss)(source, target); } // partialIndex /** Computes an index for $(D source) based on the comparison $(D less) and deposits the result in $(D target). It is acceptable that $(D target.length < source.length), in which case only the smallest $(D target.length) elements in $(D source) get indexed. The target provides a sorted "view" into $(D source). This technique is similar to sorting and partial sorting, but it is more flexible because (1) it allows "sorting" of immutable collections, (2) allows binary search even if the original collection does not offer random access, (3) allows multiple indexes, each on a different comparison criterion, (4) may be faster when dealing with large objects. However, using an index may also be slower under certain circumstances due to the extra indirection, and is always larger than a sorting-based solution because it needs space for the index in addition to the original collection. The complexity is $(BIGOH source.length * log(target.length)). Two types of indexes are accepted. They are selected by simply passing the appropriate $(D target) argument: $(OL $(LI Indexes of type $(D Iterator!(Source)), in which case the index will be sorted with the predicate $(D less(*a, *b));) $(LI Indexes of an integral type (e.g. $(D size_t)), in which case the index will be sorted with the predicate $(D less(source[a], source[b])).)) Example: ---- immutable arr = [ 2, 3, 1 ]; int* index[3]; partialIndex(arr, index); assert(*index[0] == 1 && *index[1] == 2 && *index[2] == 3); assert(isSorted!("*a < *b")(index)); ---- */ void partialIndex( alias less, SwapStrategy ss = SwapStrategy.unstable, SRange, TRange)(SRange source, TRange target) { return .topNIndexImpl!(less, true, ss)(source, target); } unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); immutable arr = [ 2, 3, 1 ]; auto index = new immutable(int)*[3]; partialIndex!(binaryFun!("a < b"))(arr, index); assert(*index[0] == 1 && *index[1] == 2 && *index[2] == 3); assert(isSorted!("*a < *b")(index)); } unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); static bool less(int a, int b) { return a < b; } { string[] x = ([ "c", "a", "b", "d" ]).dup; // test with integrals auto index1 = new size_t[x.length]; partialIndex!(q{a < b})(x, index1); assert(index1[0] == 1 && index1[1] == 2 && index1[2] == 0 && index1[3] == 3); // half-sized index1 = new size_t[x.length / 2]; partialIndex!(q{a < b})(x, index1); assert(index1[0] == 1 && index1[1] == 2); // and with iterators auto index = new string*[x.length]; partialIndex!(q{a < b})(x, index); assert(isSorted!(q{*a < *b})(index)); assert(*index[0] == "a" && *index[1] == "b" && *index[2] == "c" && *index[3] == "d"); } { immutable arr = [ 2, 3, 1 ]; auto index = new immutable(int)*[arr.length]; partialIndex!(less)(arr, index); assert(*index[0] == 1 && *index[1] == 2 && *index[2] == 3); assert(isSorted!(q{*a < *b})(index)); } // random data auto b = rndstuff!(string)(); auto index = new string*[b.length]; partialIndex!((a, b) => std.uni.toUpper(a) < std.uni.toUpper(b))(b, index); assert(isSorted!((a, b) => std.uni.toUpper(*a) < std.uni.toUpper(*b))(index)); // random data with indexes auto index1 = new size_t[b.length]; bool cmp(string x, string y) { return std.uni.toUpper(x) < std.uni.toUpper(y); } partialIndex!(cmp)(b, index1); bool check(size_t x, size_t y) { return std.uni.toUpper(b[x]) < std.uni.toUpper(b[y]); } assert(isSorted!(check)(index1)); } // Commented out for now, needs reimplementation // // schwartzMakeIndex // /** // Similar to $(D makeIndex) but using $(D schwartzSort) to sort the // index. // Example: // ---- // string[] arr = [ "ab", "c", "Ab", "C" ]; // auto index = schwartzMakeIndex!(toUpper, less, SwapStrategy.stable)(arr); // assert(*index[0] == "ab" && *index[1] == "Ab" // && *index[2] == "c" && *index[2] == "C"); // assert(isSorted!("toUpper(*a) < toUpper(*b)")(index)); // ---- // */ // Iterator!(Range)[] schwartzMakeIndex( // alias transform, // alias less, // SwapStrategy ss = SwapStrategy.unstable, // Range)(Range r) // { // alias Iter = Iterator!(Range); // auto result = new Iter[r.length]; // // assume collection already ordered // size_t i = 0; // foreach (it; begin(r) .. end(r)) // { // result[i++] = it; // } // // sort the index // alias Transformed = typeof(transform(*result[0])); // static bool indirectLess(Transformed a, Transformed b) // { // return less(a, b); // } // static Transformed indirectTransform(Iter a) // { // return transform(*a); // } // schwartzSort!(indirectTransform, less, ss)(result); // return result; // } // /// Ditto // Iterator!(Range)[] schwartzMakeIndex( // alias transform, // string less = q{a < b}, // SwapStrategy ss = SwapStrategy.unstable, // Range)(Range r) // { // return .schwartzMakeIndex!( // transform, binaryFun!(less), ss, Range)(r); // } // version (wyda) unittest // { // string[] arr = [ "D", "ab", "c", "Ab", "C" ]; // auto index = schwartzMakeIndex!(toUpper, "a < b", // SwapStrategy.stable)(arr); // assert(isSorted!(q{toUpper(*a) < toUpper(*b)})(index)); // assert(*index[0] == "ab" && *index[1] == "Ab" // && *index[2] == "c" && *index[3] == "C"); // // random data // auto b = rndstuff!(string)(); // auto index1 = schwartzMakeIndex!(toUpper)(b); // assert(isSorted!("toUpper(*a) < toUpper(*b)")(index1)); // } +/ // Internal random array generators version(unittest) { package enum size_t maxArraySize = 50; package enum size_t minArraySize = maxArraySize - 1; package string[] rndstuff(T : string)() { import std.random : Random, unpredictableSeed, uniform; static Random rnd; static bool first = true; if (first) { rnd = Random(unpredictableSeed); first = false; } string[] result = new string[uniform(minArraySize, maxArraySize, rnd)]; string alpha = "abcdefghijABCDEFGHIJ"; foreach (ref s; result) { foreach (i; 0 .. uniform(0u, 20u, rnd)) { auto j = uniform(0, alpha.length - 1, rnd); s ~= alpha[j]; } } return result; } package int[] rndstuff(T : int)() { import std.random : Random, unpredictableSeed, uniform; static Random rnd; static bool first = true; if (first) { rnd = Random(unpredictableSeed); first = false; } int[] result = new int[uniform(minArraySize, maxArraySize, rnd)]; foreach (ref i; result) { i = uniform(-100, 100, rnd); } return result; } package double[] rndstuff(T : double)() { double[] result; foreach (i; rndstuff!(int)()) { result ~= i / 50.0; } return result; } }