// Written in the D programming language. /** This module is a port of a growing fragment of the $(D_PARAM algorithm) header in Alexander Stepanov's $(LINK2 http://www.sgi.com/tech/stl/,Standard Template Library). Macros: WIKI = Phobos/StdAlgorithm Author: Andrei Alexandrescu */ /* * Copyright (C) 2004-2006 by Digital Mars, www.digitalmars.com * Written by Andrei Alexandrescu, www.erdani.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * o The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * o Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * o This notice may not be removed or altered from any source * distribution. */ module std.algorithm; private import std.stdio; private import std.math; private import std.random; private import std.date; private import std.functional; /* The iterator-related part below is undocumented and might * change in future releases. Do NOT rely on it. */ template IteratorType(T : U[], U) { alias U* IteratorType; } template ElementType(T : U[], U) { alias U ElementType; } IteratorType!(T[]) begin(T)(T[] range) { return range.ptr; } ElementType!(T[]) front(T)(T[] range) { assert(range.length); return *range.ptr; } bool isEmpty(T)(T[] range) { return !range.length; } IteratorType!(T[]) end(T)(T[] range) { return range.ptr + range.length; } void next(T)(ref T[] range) { range = range.ptr[1 .. range.length];; } IteratorType!(R) adjacentFind(R, E)(R range) { if (range.isEmpty()) return range.end(); auto result = range.begin(); range.next(); if (range.isEmpty()) return range.end(); for (; !range.isEmpty(); range.next()) { auto next = range.begin(); if (*result == *next) return result; result = next; } return range.end(); } IteratorType!(Range) find(Range, E)(Range haystack, E needle) { ElementType!(Range) e; for (; !isEmpty(haystack); next(haystack)) { if (front(haystack) == needle) break; } return begin(haystack); } unittest { int[] a = ([ 1, 2, 3 ]).dup; assert(find(a, 5) == a.ptr + a.length); assert(find(a, 2) == &a[1]); } /** Swaps $(D_PARAM lhs) and $(D_PARAM rhs). */ void swap(T)(ref T lhs, ref T rhs) { final t = lhs; lhs = rhs; rhs = t; } /** Implements C.A.R. Hoare's $(LINK2 http://en.wikipedia.org/wiki/Selection_algorithm#Partition-based_general_selection_algorithm, partition) algorithm. Specifically, reorders the range [$(D_PARAM left), $(D_PARAM right)$(RPAREN) such that everything strictly smaller (according to the predicate $(D_PARAM compare)) than $(D_PARAM *mid) is to the left of the returned pointer, and everything else is at the right of the returned pointer. Precondition: $(D_PARAM left == mid && mid == right || left <= mid && mid < right). Returns: If $(D_PARAM left == right), returns $(D_PARAM left). Otherwise, return a value $(D_PARAM p) such that the following three conditions are simultaneously true: $(OL $(LI $(D_PARAM *p == *mid)) $(LI $(D_PARAM compare(*p1, *p)) for all $(D_PARAM p1) in [$(D_PARAM left), $(D_PARAM p)$(RPAREN)) $(LI $(D_PARAM !compare(*p2, *p)) for all $(D_PARAM p2) in [$(D_PARAM p), $(D_PARAM right)$(RPAREN))) Example: ---- auto a = [3, 3, 2].dup; p = partition!(less)(a.ptr, a.ptr, a.ptr + a.length); assert(p == a.ptr + 1 && a == [2, 3, 3]); ---- */ template partition(alias compare) { /// T partition(T)(T left, T mid, T right) { if (left == right) return left; assert(left <= mid && mid < right); auto pivot = *mid; --right; swap(*mid, *right); // Move pivot to end auto result = left; for (auto i = left; i != right; ++i) { if (!compare(*i, pivot)) continue; swap(*result, *i); ++result; } swap(*right, *result); // Move pivot to its final place assert(*result == pivot); return result; } } unittest { int[] a = null; auto p = partition!(less)(a.ptr, a.ptr, a.ptr + a.length); assert(p is null); a = [2].dup; p = partition!(less)(a.ptr, a.ptr, a.ptr + a.length); assert(p == a.ptr); a = [2, 2].dup; p = partition!(less)(a.ptr, a.ptr, a.ptr + a.length); assert(p == a.ptr); p = partition!(less)(a.ptr, a.ptr + 1, a.ptr + a.length); assert(p == a.ptr); a = [3, 3, 2].dup; p = partition!(less)(a.ptr, a.ptr, a.ptr + a.length); assert(p == a.ptr + 1); } /** Reorders the range [$(D_PARAM b), $(D_PARAM e)$(RPAREN) such that $(D_PARAM nth) points to the element that would fall there if the range were fully sorted. Effectively, it finds the nth smallest (according to $(D_PARAM compare)) element in the range [$(D_PARAM b), $(D_PARAM e)$(RPAREN). Example: ---- auto v = ([ 25, 7, 9, 2, 0, 5, 21 ]).dup; auto n = 4; nthElement!(less)(v.ptr, v.ptr + n, v.ptr + v.length); assert(v[n] == 9); ---- */ template nthElement(alias compare) { /// void nthElement(T)(T b, T nth, T e) { assert(b <= nth && nth < e); for (;;) { auto pivot = b + (e - b) / 2; pivot = partition!(compare)(b, pivot, e); if (pivot == nth) return; if (pivot < nth) b = pivot + 1; else e = pivot; } } } unittest { scope(failure) writeln(stderr, "Failure testing algorithm"); auto v = ([ 25, 7, 9, 2, 0, 5, 21 ]).dup; auto n = 4; nthElement!(less)(v.ptr, v.ptr + n, v.ptr + v.length); assert(v[n] == 9); // v = ([3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]).dup; n = 3; nthElement!(less)(v.ptr, v.ptr + n, v.ptr + v.length); assert(v[n] == 3); // v = ([3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]).dup; n = 1; nthElement!(less)(v.ptr, v.ptr + n, v.ptr + v.length); assert(v[n] == 2); // v = ([3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]).dup; n = v.length - 1; nthElement!(less)(v.ptr, v.ptr + n, v.ptr + v.length); assert(v[n] == 7); // v = ([3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]).dup; n = 0; nthElement!(less)(v.ptr, v.ptr + n, v.ptr + v.length); assert(v[n] == 1); } /** Reverses $(D_PARAM range) in-place. */ void reverse(T)(T range) { auto len = range.length; final limit = len / 2; --len; for (uint i = 0; i != limit; ++i) { final t = range[i]; range[i] = range[len - i]; range[len - i] = t; } } unittest { int[] range = null; reverse(range); range = [ 1 ].dup; reverse(range); assert(range == [1]); range = [1, 2].dup; reverse(range); assert(range == [2, 1]); range = [1, 2, 3].dup; reverse(range); assert(range == [3, 2, 1]); } /** Sorts a random-access range according to predicate $(D_PARAM comp), which must be a function name. Example: ---- int[] array = ([ 1, 2, 3, 4 ]).dup; sort!(greater)(array); assert(array == [ 4, 3, 2, 1 ]); bool myComp(int x, int y) { return x < y; } sort!(myComp)(array); assert(array == [ 1, 2, 3, 4 ]); ---- */ template sort(alias comp) { void sort(Range)(Range r) { static if (is(typeof(comp(*begin(r), *end(r))) == bool)) { sortImpl!(comp)(begin(r), end(r)); assert(isSorted!(comp)(r)); } else { static assert(false, typeof(&comp).stringof); } } } /** Sorts a random-access range according to predicate $(D_PARAM comp), expressed as a string. The string can use the names "a" and "b" for the two elements being compared. Example: ---- int[] array = ([ 1, 2, 3, 4 ]).dup; sort!("a > b")(array); assert(array == [ 4, 3, 2, 1 ]); ---- */ template sort(string comp) { void sort(Range)(Range r) { alias typeof(*begin(r)) ElementType; static ElementType a, b; alias typeof(mixin(comp)) ResultType; static ResultType compFn(ElementType a, ElementType b) { return mixin(comp); } .sort!(compFn)(r); } } unittest { // sort using delegate int a[] = new int[100]; auto rnd = Random(getUTCtime); foreach (ref e; a) { e = uniform!(int)(rnd, -100, 100); } int i = 0; bool greater2(int a, int b) { return a + i > b + i; } bool delegate(int, int) greater = &greater2; sort!(greater)(a); assert(isSorted!(greater)(a)); // sort using string sort!("a < b")(a); assert(isSorted!(less)(a)); // sort using function; all elements equal foreach (ref e; a) { e = 5; } sort!(less)(a); assert(isSorted!(less)(a)); // sort using ternary predicate //sort!("b - a")(a); //assert(isSorted!(less)(a)); } /*private*/ template getPivot(alias compare) { Iter getPivot(Iter)(Iter b, Iter e) { final r = b + (e - b) / 2; return r; } } /*private*/ template sortImpl(alias comp) { void sortImpl(Iter)(Iter b, Iter e) { while (e - b > 1) { auto m = partition!(comp)(b, getPivot!(comp)(b, e), e); assert(b <= m && m < e); .sortImpl!(comp)(b, m); b = ++m; } } } /** Checks whether a random-access range is sorted according to the comparison operation $(D_PARAM comp). Example: ---- int[] arr = ([4, 3, 2, 1]).dup; assert(!isSorted!(less)(arr)); sort!(less)(arr); assert(isSorted!(less)(arr)); ---- */ template isSorted(alias comp) { bool isSorted(Range)(Range r) { auto b = begin(r), e = end(r); if (e - b <= 1) return true; auto next = b + 1; for (; next < e; ++b, ++next) { if (comp(*next, *b)) return false; } return true; } }