tanya/source/tanya/meta/metafunction.d
2017-08-27 15:32:05 +02:00

1510 lines
37 KiB
D

/* 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/. */
/**
* This module contains functions that manipulate template type lists as well
* as algorithms to perform arbitrary compile-time computations.
*
* 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)
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/meta/metafunction.d,
* tanya/meta/metafunction.d)
*/
module tanya.meta.metafunction;
import tanya.meta.trait;
template Min(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred)
{
static if (Args.length == 1)
{
alias Min = Alias!(Args[0]);
}
else static if (isLess!(pred, Args[1], Args[0]))
{
alias Min = Min!(pred, Args[1], Args[2 .. $]);
}
else
{
alias Min = Min!(pred, Args[0], Args[2 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
enum bool cmp(alias T, alias U) = T < U;
static assert(Min!(cmp, 8, 4, 5, 3, 13) == 3);
static assert(Min!(cmp, 8) == 8);
}
template Max(alias pred, Args...)
if (Args.length > 0 && isTemplate!pred)
{
static if (Args.length == 1)
{
alias Max = Alias!(Args[0]);
}
else static if (isGreater!(pred, Args[1], Args[0]))
{
alias Max = Max!(pred, Args[1], Args[2 .. $]);
}
else
{
alias Max = Max!(pred, Args[0], Args[2 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
enum bool cmp(alias T, alias U) = T < U;
static assert(Max!(cmp, 8, 4, 5, 3, 13) == 13);
static assert(Max!(cmp, 8) == 8);
}
template ZipWith(alias pred, Tuples...)
if (Tuples.length > 0
&& isTemplate!pred
&& allSatisfy!(ApplyLeft!(isInstanceOf, AliasTuple), Tuples))
{
private template GetIth(size_t i, Args...)
{
static if ((Args.length == 0) || (Args[0].Seq.length <= i))
{
alias GetIth = AliasSeq!();
}
else
{
alias GetIth = AliasSeq!(Args[0].Seq[i], GetIth!(i, Args[1 .. $]));
}
}
private template Iterate(size_t i, Args...)
{
alias Tuple = GetIth!(i, Args);
static if (Tuple.length < Tuples.length)
{
alias Iterate = AliasSeq!();
}
else
{
alias Iterate = AliasSeq!(pred!Tuple,
Iterate!(i + 1, Args));
}
}
alias ZipWith = Iterate!(0, Tuples);
}
///
pure nothrow @safe @nogc unittest
{
alias Result1 = ZipWith!(AliasSeq,
AliasTuple!(1, 2),
AliasTuple!(5, 6),
AliasTuple!(9, 10));
static assert(Result1 == AliasSeq!(1, 5, 9, 2, 6, 10));
alias Result2 = ZipWith!(AliasSeq,
AliasTuple!(1, 2, 3),
AliasTuple!(4, 5));
static assert(Result2 == AliasSeq!(1, 4, 2, 5));
alias Result3 = ZipWith!(AliasSeq, AliasTuple!(), AliasTuple!(4, 5));
static assert(Result3.length == 0);
}
/**
* Holds a typed sequence of template parameters.
*
* Different than $(D_PSYMBOL AliasSeq), $(D_PSYMBOL AliasTuple) doesn't unpack
* its template parameters automatically. Consider:
*
* ---
* template A(Args...)
* {
* static assert(Args.length == 4);
* }
*
* alias AInstance = A!(AliasSeq!(int, uint), AliasSeq!(float, double));
* ---
*
* Using $(D_PSYMBOL AliasSeq) template `A` gets 4 parameters instead of 2,
* because $(D_PSYMBOL AliasSeq) is just an alias for its template parameters.
*
* With $(D_PSYMBOL AliasTuple) it is possible to pass distinguishable
* sequences of parameters to a template. So:
*
* ---
* template B(Args...)
* {
* static assert(Args.length == 2);
* }
*
* alias BInstance = B!(AliasTuple!(int, uint), AliasTuple!(float, double));
* ---
*
* Params:
* Args = Elements of this $(D_PSYMBOL AliasTuple).
*
* See_Also: $(D_PSYMBOL AliasSeq).
*/
template AliasTuple(Args...)
{
/// Elements in this tuple as $(D_PSYMBOL AliasSeq).
alias Seq = Args;
/// The length of the tuple.
enum size_t length = Args.length;
}
///
pure nothrow @safe @nogc unittest
{
alias A = AliasTuple!(short);
alias B = AliasTuple!(3, 8, 9);
alias C = AliasTuple!(A, B);
static assert(C.length == 2);
static assert(A.length == 1);
static assert(is(A.Seq == AliasSeq!short));
static assert(B.length == 3);
static assert(B.Seq == AliasSeq!(3, 8, 9));
alias D = AliasTuple!();
static assert(D.length == 0);
static assert(is(D.Seq == AliasSeq!()));
}
/**
* Tests whether $(D_INLINECODE Args[0]) is less than or equal to
* $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE Args[0] < Args[1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE Args[0] < Args[1]), a positive number that
* $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
* )
*
* Params:
* Args = Two aliases to compare for equality.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than or equal
* to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isLessEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
{
private enum result = cmp!(Args[1], Args[0]);
static if (is(typeof(result) == bool))
{
enum bool isLessEqual = !result;
}
else
{
enum bool isLessEqual = result >= 0;
}
}
///
pure nothrow @safe @nogc unittest
{
enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(isLessEqual!(boolCmp, byte, int));
static assert(isLessEqual!(boolCmp, uint, int));
static assert(!isLessEqual!(boolCmp, long, int));
enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
static assert(isLessEqual!(intCmp, byte, int));
static assert(isLessEqual!(intCmp, uint, int));
static assert(!isLessEqual!(intCmp, long, int));
}
/**
* Tests whether $(D_INLINECODE Args[0]) is greater than or equal to
* $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE Args[0] < Args[1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE Args[0] < Args[1]), a positive number that
* $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
* )
*
* Params:
* Args = Two aliases to compare for equality.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than or
* equal to $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isGreaterEqual(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
{
enum bool isGreaterEqual = !result;
}
else
{
enum bool isGreaterEqual = result >= 0;
}
}
///
pure nothrow @safe @nogc unittest
{
enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(!isGreaterEqual!(boolCmp, byte, int));
static assert(isGreaterEqual!(boolCmp, uint, int));
static assert(isGreaterEqual!(boolCmp, long, int));
enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
static assert(!isGreaterEqual!(intCmp, byte, int));
static assert(isGreaterEqual!(intCmp, uint, int));
static assert(isGreaterEqual!(intCmp, long, int));
}
/**
* Tests whether $(D_INLINECODE Args[0]) is less than
* $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE Args[0] < Args[1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE Args[0] < Args[1]), a positive number that
* $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
* )
*
* Params:
* Args = Two aliases to compare for equality.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is less than
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isLess(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
{
enum bool isLess = result;
}
else
{
enum bool isLess = result < 0;
}
}
///
pure nothrow @safe @nogc unittest
{
enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(isLess!(boolCmp, byte, int));
static assert(!isLess!(boolCmp, uint, int));
static assert(!isLess!(boolCmp, long, int));
enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
static assert(isLess!(intCmp, byte, int));
static assert(!isLess!(intCmp, uint, int));
static assert(!isLess!(intCmp, long, int));
}
/**
* Tests whether $(D_INLINECODE Args[0]) is greater than
* $(D_INLINECODE Args[1]) according to $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE Args[0] < Args[1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE Args[0] < Args[1]), a positive number that
* $(D_INLINECODE Args[0] > Args[1]), `0` if they equal.)
* )
*
* Params:
* Args = Two aliases to compare for equality.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is greater than
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isGreater(alias cmp, Args...)
if (Args.length == 2 && isTemplate!cmp)
{
private enum result = cmp!Args;
static if (is(typeof(result) == bool))
{
enum bool isGreater = !result && cmp!(Args[1], Args[0]);
}
else
{
enum bool isGreater = result > 0;
}
}
///
pure nothrow @safe @nogc unittest
{
enum bool boolCmp(T, U) = T.sizeof < U.sizeof;
static assert(!isGreater!(boolCmp, byte, int));
static assert(!isGreater!(boolCmp, uint, int));
static assert(isGreater!(boolCmp, long, int));
enum ptrdiff_t intCmp(T, U) = T.sizeof - U.sizeof;
static assert(!isGreater!(intCmp, byte, int));
static assert(!isGreater!(intCmp, uint, int));
static assert(isGreater!(intCmp, long, int));
}
/**
* Tests whether $(D_INLINECODE Args[0]) is equal to $(D_INLINECODE Args[1]).
*
* $(D_PSYMBOL isEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types:
* $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are
* considered to be not equal.
*
* Params:
* Args = Two aliases to compare for equality.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) is equal to
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isEqual(Args...)
if (Args.length == 2)
{
static if ((is(typeof(Args[0] == Args[1])) && (Args[0] == Args[1]))
|| is(Args[0] == Args[1]))
{
enum bool isEqual = true;
}
else
{
enum bool isEqual = false;
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(isEqual!(int, int));
static assert(!isEqual!(5, int));
static assert(!isEqual!(5, 8));
}
/**
* Tests whether $(D_INLINECODE Args[0]) isn't equal to
* $(D_INLINECODE Args[1]).
*
* $(D_PSYMBOL isNotEqual) checks first if $(D_PARAM Args) can be compared directly. If not, they are compared as types:
* $(D_INLINECODE is(Args[0] == Args[1])). It it fails, the arguments are
* considered to be not equal.
*
* Params:
* Args = Two aliases to compare for equality.
*
* Returns: $(D_KEYWORD true) if $(D_INLINECODE Args[0]) isn't equal to
* $(D_INLINECODE Args[1]), $(D_KEYWORD false) otherwise.
*/
template isNotEqual(Args...)
if (Args.length == 2)
{
enum bool isNotEqual = !isEqual!Args;
}
///
pure nothrow @safe @nogc unittest
{
static assert(!isNotEqual!(int, int));
static assert(isNotEqual!(5, int));
static assert(isNotEqual!(5, 8));
}
/**
* Instantiates the template $(D_PARAM T) with $(D_PARAM ARGS).
*
* Params:
* T = Template.
* Args = Template parameters.
*
* Returns: Instantiated template.
*/
alias Instantiate(alias T, Args...) = T!Args;
version (TanyaPhobos)
{
public import std.meta : Alias,
AliasSeq,
aliasSeqOf,
Erase,
EraseAll,
Filter,
NoDuplicates,
DerivedToFront,
MostDerived,
Repeat,
Replace,
ReplaceAll,
Reverse,
Map = staticMap,
Sort = staticSort,
allSatisfy,
anySatisfy,
staticIndexOf,
templateAnd,
templateNot,
templateOr,
isSorted = staticIsSorted,
ApplyLeft,
ApplyRight;
}
else:
import tanya.meta.trait;
import tanya.meta.transform;
/**
* Creates an alias for $(D_PARAM T).
*
* In contrast to the $(D_KEYWORD alias)-keyword $(D_PSYMBOL Alias) can alias
* any kind of D symbol that can be used as argument to template alias
* parameters.
*
* $(UL
* $(LI Types)
* $(LI Local and global names)
* $(LI Module names)
* $(LI Template names)
* $(LI Template instance names)
* $(LI Literals)
* )
*
* Params:
* T = A symbol.
*
* Returns: An alias for $(D_PARAM T).
*
* See_Also: $(LINK2 https://dlang.org/spec/template.html#aliasparameters,
* Template Alias Parameters).
*/
alias Alias(alias T) = T;
/// Ditto.
alias Alias(T) = T;
///
pure nothrow @safe @nogc unittest
{
static assert(is(Alias!int));
static assert(is(typeof(Alias!5)));
static assert(is(typeof(Alias!(() {}))));
int i;
static assert(is(typeof(Alias!i)));
}
/**
* Holds a sequence of aliases.
*
* $(D_PSYMBOL AliasSeq) can be used to pass multiple parameters to a template
* at once. $(D_PSYMBOL AliasSeq) behaves as it were just $(D_PARAM Args). Note
* that because of this property, if multiple instances of
* $(D_PSYMBOL AliasSeq) are passed to a template, they are not distinguishable
* from each other and act as a single sequence. There is also no way to make
* $(D_PSYMBOL AliasSeq) nested, it always unpacks its elements.
*
* Params:
* Args = Symbol sequence.
*
* Returns: An alias for sequence $(D_PARAM Args).
*
* See_Also: $(D_PSYMBOL Alias).
*/
alias AliasSeq(Args...) = Args;
///
pure nothrow @safe @nogc unittest
{
static assert(is(typeof({ alias T = AliasSeq!(short, 5); })));
static assert(is(typeof({ alias T = AliasSeq!(int, short, 5); })));
static assert(is(typeof({ alias T = AliasSeq!(() {}, short, 5); })));
static assert(is(typeof({ alias T = AliasSeq!(); })));
static assert(AliasSeq!().length == 0);
static assert(AliasSeq!(int, short, 5).length == 3);
alias A = AliasSeq!(short, float);
alias B = AliasSeq!(ushort, double);
alias C = AliasSeq!(A, B);
static assert(C.length == 4);
}
/**
* Tests whether all the items of $(D_PARAM L) satisfy the condition
* $(D_PARAM F).
*
* $(D_PARAM F) is a template that accepts one parameter and returns a boolean,
* so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called.
*
* Params:
* F = Template predicate.
* L = List of items to test.
*
* Returns: $(D_KEYWORD true) if all the items of $(D_PARAM L) satisfy
* $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/
template allSatisfy(alias F, L...)
if (isTemplate!F)
{
static if (L.length == 0)
{
enum bool allSatisfy = true;
}
else static if (F!(L[0]))
{
enum bool allSatisfy = allSatisfy!(F, L[1 .. $]);
}
else
{
enum bool allSatisfy = false;
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(allSatisfy!(isSigned, int, short, byte, long));
static assert(!allSatisfy!(isUnsigned, uint, ushort, float, ulong));
}
/**
* Tests whether any of the items of $(D_PARAM L) satisfy the condition
* $(D_PARAM F).
*
* $(D_PARAM F) is a template that accepts one parameter and returns a boolean,
* so $(D_INLINECODE F!([0]) && F!([1])) and so on, can be called.
*
* Params:
* F = Template predicate.
* L = List of items to test.
*
* Returns: $(D_KEYWORD true) if any of the items of $(D_PARAM L) satisfy
* $(D_PARAM F), $(D_KEYWORD false) otherwise.
*/
template anySatisfy(alias F, L...)
if (isTemplate!F)
{
static if (L.length == 0)
{
enum bool anySatisfy = false;
}
else static if (F!(L[0]))
{
enum bool anySatisfy = true;
}
else
{
enum bool anySatisfy = anySatisfy!(F, L[1 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(anySatisfy!(isSigned, int, short, byte, long));
static assert(anySatisfy!(isUnsigned, uint, ushort, float, ulong));
static assert(!anySatisfy!(isSigned, uint, ushort, ulong));
}
private template indexOf(ptrdiff_t i, Args...)
if (Args.length > 0)
{
static if (Args.length == 1)
{
enum ptrdiff_t indexOf = -1;
}
else static if (isEqual!(Args[0 .. 2]))
{
enum ptrdiff_t indexOf = i;
}
else
{
enum ptrdiff_t indexOf = indexOf!(i + 1,
AliasSeq!(Args[0], Args[2 .. $]));
}
}
/**
* Returns the index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
* `-1` is returned if $(D_PARAM T) is not found.
*
* Params:
* T = The type to search for.
* L = Type list.
*
* Returns: The index of the first occurrence of $(D_PARAM T) in $(D_PARAM L).
*/
template staticIndexOf(T, L...)
{
enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
}
/// Ditto.
template staticIndexOf(alias T, L...)
{
enum ptrdiff_t staticIndexOf = indexOf!(0, AliasSeq!(T, L));
}
///
pure nothrow @safe @nogc unittest
{
static assert(staticIndexOf!(int) == -1);
static assert(staticIndexOf!(int, int) == 0);
static assert(staticIndexOf!(int, float, double, int, real) == 2);
static assert(staticIndexOf!(3, () {}, uint, 5, 3) == 3);
}
/**
* Combines multiple templates with logical AND. So $(D_PSYMBOL templateAnd)
* evaluates to $(D_INLINECODE Preds[0] && Preds[1] && Preds[2]) and so on.
*
* Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD true).
*
* Params:
* Preds = Template predicates.
*
* Returns: The constructed template.
*/
template templateAnd(Preds...)
if (allSatisfy!(isTemplate, Preds))
{
template templateAnd(T...)
{
static if (Preds.length == 0)
{
enum bool templateAnd = true;
}
else static if (Instantiate!(Preds[0], T))
{
alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T);
}
else
{
enum bool templateAnd = false;
}
}
}
///
pure nothrow @safe @nogc unittest
{
alias isMutableInt = templateAnd!(isIntegral, isMutable);
static assert(isMutableInt!int);
static assert(!isMutableInt!(const int));
static assert(!isMutableInt!float);
alias alwaysTrue = templateAnd!();
static assert(alwaysTrue!int);
alias isIntegral = templateAnd!(.isIntegral);
static assert(isIntegral!int);
static assert(isIntegral!(const int));
static assert(!isIntegral!float);
}
/**
* Combines multiple templates with logical OR. So $(D_PSYMBOL templateOr)
* evaluates to $(D_INLINECODE Preds[0] || Preds[1] || Preds[2]) and so on.
*
* Empty $(D_PARAM Preds) evaluates to $(D_KEYWORD false).
*
* Params:
* Preds = Template predicates.
*
* Returns: The constructed template.
*/
template templateOr(Preds...)
if (allSatisfy!(isTemplate, Preds))
{
template templateOr(T...)
{
static if (Preds.length == 0)
{
enum bool templateOr = false;
}
else static if (Instantiate!(Preds[0], T))
{
enum bool templateOr = true;
}
else
{
alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T);
}
}
}
///
pure nothrow @safe @nogc unittest
{
alias isMutableOrInt = templateOr!(isIntegral, isMutable);
static assert(isMutableOrInt!int);
static assert(isMutableOrInt!(const int));
static assert(isMutableOrInt!float);
static assert(!isMutableOrInt!(const float));
alias alwaysFalse = templateOr!();
static assert(!alwaysFalse!int);
alias isIntegral = templateOr!(.isIntegral);
static assert(isIntegral!int);
static assert(isIntegral!(const int));
static assert(!isIntegral!float);
}
/**
* Params:
* pred = Template predicate.
*
* Returns: Negated $(D_PARAM pred).
*/
template templateNot(alias pred)
if (isTemplate!pred)
{
enum bool templateNot(T...) = !pred!T;
}
///
pure nothrow @safe @nogc unittest
{
alias isNotIntegral = templateNot!isIntegral;
static assert(!isNotIntegral!int);
static assert(isNotIntegral!(char[]));
}
/**
* Tests whether $(D_PARAM L) is sorted in ascending order according to
* $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE a[i] < a[i + 1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE a[i] < a[i + 1]), a positive number that
* $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.)
* )
*
* Params:
* cmp = Sorting template predicate.
* L = Elements to be tested.
*
* Returns: $(D_KEYWORD true) if $(D_PARAM L) is sorted, $(D_KEYWORD false)
* if not.
*/
template isSorted(alias cmp, L...)
if (isTemplate!cmp)
{
static if (L.length <= 1)
{
enum bool isSorted = true;
}
else
{
// `L` is sorted if the both halves and the boundary values are sorted.
enum bool isSorted = isLessEqual!(cmp, L[$ / 2 - 1], L[$ / 2])
&& isSorted!(cmp, L[0 .. $ / 2])
&& isSorted!(cmp, L[$ / 2 .. $]);
}
}
deprecated("Use tanya.meta.metafunction.isSorted instead")
alias staticIsSorted = isSorted;
///
pure nothrow @safe @nogc unittest
{
enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(isSorted!(cmp));
static assert(isSorted!(cmp, byte));
static assert(isSorted!(cmp, byte, ubyte, short, uint));
static assert(!isSorted!(cmp, long, byte, ubyte, short, uint));
}
private pure nothrow @safe @nogc unittest
{
enum cmp(int x, int y) = x - y;
static assert(isSorted!(cmp));
static assert(isSorted!(cmp, 1));
static assert(isSorted!(cmp, 1, 2, 2));
static assert(isSorted!(cmp, 1, 2, 2, 4));
static assert(isSorted!(cmp, 1, 2, 2, 4, 8));
static assert(!isSorted!(cmp, 32, 2, 2, 4, 8));
static assert(isSorted!(cmp, 32, 32));
}
private pure nothrow @safe @nogc unittest
{
enum cmp(int x, int y) = x < y;
static assert(isSorted!(cmp));
static assert(isSorted!(cmp, 1));
static assert(isSorted!(cmp, 1, 2, 2));
static assert(isSorted!(cmp, 1, 2, 2, 4));
static assert(isSorted!(cmp, 1, 2, 2, 4, 8));
static assert(!isSorted!(cmp, 32, 2, 2, 4, 8));
static assert(isSorted!(cmp, 32, 32));
}
/**
* Params:
* T = A template.
* Args = The first arguments for $(D_PARAM T).
*
* Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as its first
* arguments.
*/
template ApplyLeft(alias T, Args...)
{
alias ApplyLeft(U...) = T!(Args, U);
}
///
pure nothrow @safe @nogc unittest
{
alias allAreIntegral = ApplyLeft!(allSatisfy, isIntegral);
static assert(allAreIntegral!(int, uint));
static assert(!allAreIntegral!(int, float, uint));
}
/**
* Params:
* T = A template.
* Args = The last arguments for $(D_PARAM T).
*
* Returns: $(D_PARAM T) with $(D_PARAM Args) applied to it as itslast
* arguments.
*/
template ApplyRight(alias T, Args...)
{
alias ApplyRight(U...) = T!(U, Args);
}
///
pure nothrow @safe @nogc unittest
{
alias intIs = ApplyRight!(allSatisfy, int);
static assert(intIs!(isIntegral));
static assert(!intIs!(isUnsigned));
}
/**
* Params:
* n = The number of times to repeat $(D_PARAM L).
* L = The sequence to be repeated.
*
* Returns: $(D_PARAM L) repeated $(D_PARAM n) times.
*/
template Repeat(size_t n, L...)
if (n > 0)
{
static if (n == 1)
{
alias Repeat = L;
}
else
{
alias Repeat = AliasSeq!(L, Repeat!(n - 1, L));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Repeat!(1, uint, int) == AliasSeq!(uint, int)));
static assert(is(Repeat!(2, uint, int) == AliasSeq!(uint, int, uint, int)));
static assert(is(Repeat!(3) == AliasSeq!()));
}
private template ReplaceOne(L...)
{
static if (L.length == 2)
{
alias ReplaceOne = AliasSeq!();
}
else static if (isEqual!(L[0], L[2]))
{
alias ReplaceOne = AliasSeq!(L[1], L[3 .. $]);
}
else
{
alias ReplaceOne = AliasSeq!(L[2], ReplaceOne!(L[0], L[1], L[3 .. $]));
}
}
/**
* Replaces the first occurrence of $(D_PARAM T) in $(D_PARAM L) with
* $(D_PARAM U).
*
* Params:
* T = The symbol to be replaced.
* U = Replacement.
* L = List of symbols.
*
* Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) replaced.
*/
template Replace(T, U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
template Replace(alias T, U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
template Replace(T, alias U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
/// Ditto.
template Replace(alias T, alias U, L...)
{
alias Replace = ReplaceOne!(T, U, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Replace!(int, uint, int) == AliasSeq!(uint)));
static assert(is(Replace!(int, uint, short, int, int, ushort)
== AliasSeq!(short, uint, int, ushort)));
static assert(Replace!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 5));
}
private template ReplaceAllImpl(L...)
{
static if (L.length == 2)
{
alias ReplaceAllImpl = AliasSeq!();
}
else
{
private alias Rest = ReplaceAllImpl!(L[0], L[1], L[3 .. $]);
static if (isEqual!(L[0], L[2]))
{
alias ReplaceAllImpl = AliasSeq!(L[1], Rest);
}
else
{
alias ReplaceAllImpl = AliasSeq!(L[2], Rest);
}
}
}
/**
* Replaces all occurrences of $(D_PARAM T) in $(D_PARAM L) with $(D_PARAM U).
*
* Params:
* T = The symbol to be replaced.
* U = Replacement.
* L = List of symbols.
*
* Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) replaced.
*/
template ReplaceAll(T, U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
template ReplaceAll(alias T, U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
template ReplaceAll(T, alias U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
/// Ditto.
template ReplaceAll(alias T, alias U, L...)
{
alias ReplaceAll = ReplaceAllImpl!(T, U, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(ReplaceAll!(int, uint, int) == AliasSeq!(uint)));
static assert(is(ReplaceAll!(int, uint, short, int, int, ushort)
== AliasSeq!(short, uint, uint, ushort)));
static assert(ReplaceAll!(5, 8, 1, 2, 5, 5) == AliasSeq!(1, 2, 8, 8));
}
/**
* Params:
* L = List of symbols.
*
* Returns: $(D_PARAM L) with elements in reversed order.
*/
template Reverse(L...)
{
static if (L.length == 0)
{
alias Reverse = AliasSeq!();
}
else
{
alias Reverse = AliasSeq!(L[$ - 1], Reverse!(L[0 .. $ - 1]));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Reverse!(byte, short, int) == AliasSeq!(int, short, byte)));
}
/**
* Applies $(D_PARAM F) to all elements of $(D_PARAM T).
*
* Params:
* F = Template predicate.
* T = List of symbols.
*
* Returns: Elements $(D_PARAM T) after applying $(D_PARAM F) to them.
*/
template Map(alias F, T...)
if (isTemplate!F)
{
static if (T.length == 0)
{
alias Map = AliasSeq!();
}
else
{
alias Map = AliasSeq!(F!(T[0]), Map!(F, T[1 .. $]));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Map!(Unqual, const int, immutable short)
== AliasSeq!(int, short)));
}
/**
* Sorts $(D_PARAM L) in ascending order according to $(D_PARAM cmp).
*
* $(D_PARAM cmp) can evaluate to:
* $(UL
* $(LI $(D_KEYWORD bool): $(D_KEYWORD true) means
* $(D_INLINECODE a[i] < a[i + 1]).)
* $(LI $(D_KEYWORD int): a negative number means that
* $(D_INLINECODE a[i] < a[i + 1]), a positive number that
* $(D_INLINECODE a[i] > a[i + 1]), `0` if they equal.)
* )
*
* Merge sort is used to sort the arguments.
*
* Params:
* cmp = Sorting template predicate.
* L = Elements to be sorted.
*
* Returns: Elements of $(D_PARAM L) in ascending order.
*
* See_Also: $(LINK2 https://en.wikipedia.org/wiki/Merge_sort, Merge sort).
*/
template Sort(alias cmp, L...)
if (isTemplate!cmp)
{
private template merge(size_t A, size_t B)
{
static if (A + B == L.length)
{
alias merge = AliasSeq!();
}
else static if (B >= Right.length
|| (A < Left.length && isLessEqual!(cmp, Left[A], Right[B])))
{
alias merge = AliasSeq!(Left[A], merge!(A + 1, B));
}
else
{
alias merge = AliasSeq!(Right[B], merge!(A, B + 1));
}
}
static if (L.length <= 1)
{
alias Sort = L;
}
else
{
private alias Left = Sort!(cmp, L[0 .. $ / 2]);
private alias Right = Sort!(cmp, L[$ / 2 .. $]);
alias Sort = merge!(0, 0);
}
}
///
pure nothrow @safe @nogc unittest
{
enum cmp(T, U) = T.sizeof < U.sizeof;
static assert(is(Sort!(cmp, long, short, byte, int)
== AliasSeq!(byte, short, int, long)));
}
private pure nothrow @safe @nogc unittest
{
enum cmp(int T, int U) = T - U;
static assert(Sort!(cmp, 5, 17, 9, 12, 2, 10, 14)
== AliasSeq!(2, 5, 9, 10, 12, 14, 17));
}
private enum bool DerivedToFrontCmp(A, B) = is(A : B);
/**
* Returns $(D_PARAM L) sorted in such a way that the most derived types come
* first.
*
* Params:
* L = Type tuple.
*
* Returns: Sorted $(D_PARAM L).
*/
template DerivedToFront(L...)
{
alias DerivedToFront = Sort!(DerivedToFrontCmp, L);
}
///
pure nothrow @safe @nogc unittest
{
class A
{
}
class B : A
{
}
class C : B
{
}
static assert(is(DerivedToFront!(B, A, C) == AliasSeq!(C, B, A)));
}
/**
* Returns the type from the type tuple $(D_PARAM L) that is most derived from
* $(D_PARAM T).
*
* Params:
* T = The type to compare to.
* L = Type tuple.
*
* Returns: The type most derived from $(D_PARAM T).
*/
template MostDerived(T, L...)
{
static if (L.length == 0)
{
alias MostDerived = T;
}
else static if (is(T : L[0]))
{
alias MostDerived = MostDerived!(T, L[1 .. $]);
}
else
{
alias MostDerived = MostDerived!(L[0], L[1 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
class A
{
}
class B : A
{
}
class C : B
{
}
static assert(is(MostDerived!(A, C, B) == C));
}
private template EraseOne(L...)
if (L.length > 0)
{
static if (L.length == 1)
{
alias EraseOne = AliasSeq!();
}
else static if (isEqual!(L[0 .. 2]))
{
alias EraseOne = AliasSeq!(L[2 .. $]);
}
else
{
alias EraseOne = AliasSeq!(L[1], EraseOne!(L[0], L[2 .. $]));
}
}
/**
* Removes the first occurrence of $(D_PARAM T) from the alias sequence
* $(D_PARAL L).
*
* Params:
* T = The item to be removed.
* L = Alias sequence.
*
* Returns: $(D_PARAM L) with the first occurrence of $(D_PARAM T) removed.
*/
template Erase(T, L...)
{
alias Erase = EraseOne!(T, L);
}
/// Ditto.
template Erase(alias T, L...)
{
alias Erase = EraseOne!(T, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Erase!(int, short, int, int, uint) == AliasSeq!(short, int, uint)));
static assert(is(Erase!(int, short, uint) == AliasSeq!(short, uint)));
}
private template EraseAllImpl(L...)
{
static if (L.length == 1)
{
alias EraseAllImpl = AliasSeq!();
}
else static if (isEqual!(L[0 .. 2]))
{
alias EraseAllImpl = EraseAllImpl!(L[0], L[2 .. $]);
}
else
{
alias EraseAllImpl = AliasSeq!(L[1], EraseAllImpl!(L[0], L[2 .. $]));
}
}
/**
* Removes all occurrences of $(D_PARAM T) from the alias sequence $(D_PARAL L).
*
* Params:
* T = The item to be removed.
* L = Alias sequence.
*
* Returns: $(D_PARAM L) with all occurrences of $(D_PARAM T) removed.
*/
template EraseAll(T, L...)
{
alias EraseAll = EraseAllImpl!(T, L);
}
/// Ditto.
template EraseAll(alias T, L...)
{
alias EraseAll = EraseAllImpl!(T, L);
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(EraseAll!(int, short, int, int, uint) == AliasSeq!(short, uint)));
static assert(is(EraseAll!(int, short, uint) == AliasSeq!(short, uint)));
static assert(is(EraseAll!(int, int, int) == AliasSeq!()));
}
/**
* Returns an alias sequence which contains only items that satisfy the
* condition $(D_PARAM pred).
*
* Params:
* pred = Template predicate.
* L = Alias sequence.
*
* Returns: $(D_PARAM L) filtered so that it contains only items that satisfy
* $(D_PARAM pred).
*/
template Filter(alias pred, L...)
{
static if (L.length == 0)
{
alias Filter = AliasSeq!();
}
else static if (pred!(L[0]))
{
alias Filter = AliasSeq!(L[0], Filter!(pred, L[1 .. $]));
}
else
{
alias Filter = Filter!(pred, L[1 .. $]);
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(is(Filter!(isIntegral, real, int, bool, uint) == AliasSeq!(int, uint)));
}
/**
* Removes all duplicates from the alias sequence $(D_PARAM L).
*
* Params:
* L = Alias sequence.
*
* Returns: $(D_PARAM L) containing only unique items.
*/
template NoDuplicates(L...)
{
static if (L.length == 0)
{
alias NoDuplicates = AliasSeq!();
}
else
{
private alias Rest = NoDuplicates!(EraseAll!(L[0], L[1 .. $]));
alias NoDuplicates = AliasSeq!(L[0], Rest);
}
}
///
pure nothrow @safe @nogc unittest
{
alias Types = AliasSeq!(int, uint, int, short, short, uint);
static assert(is(NoDuplicates!Types == AliasSeq!(int, uint, short)));
}
/**
* Converts an input range $(D_PARAM range) into an alias sequence.
*
* Params:
* range = Input range.
*
* Returns: Alias sequence with items from $(D_PARAM range).
*/
template aliasSeqOf(alias range)
{
static if (isArray!(typeof(range)))
{
static if (range.length == 0)
{
alias aliasSeqOf = AliasSeq!();
}
else
{
alias aliasSeqOf = AliasSeq!(range[0], aliasSeqOf!(range[1 .. $]));
}
}
else
{
ReturnType!(typeof(&range.front))[] toArray(typeof(range) range)
{
typeof(return) result;
foreach (r; range)
{
result ~= r;
}
return result;
}
alias aliasSeqOf = aliasSeqOf!(toArray(range));
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(aliasSeqOf!([0, 1, 2, 3]) == AliasSeq!(0, 1, 2, 3));
}
/**
* Produces a alias sequence consisting of every $(D_PARAM n)th element of
* $(D_PARAM Args), starting with the first.
*
* Params:
* n = Step.
* Args = The items to stride.
*
* Returns: Alias sequence of every $(D_PARAM n)th element of $(D_PARAM Args).
*/
template Stride(size_t n, Args...)
if (n > 0)
{
static if (Args.length > n)
{
alias Stride = AliasSeq!(Args[0], Stride!(n, Args[n .. $]));
}
else static if (Args.length > 0)
{
alias Stride = AliasSeq!(Args[0]);
}
else
{
alias Stride = AliasSeq!();
}
}
///
pure nothrow @safe @nogc unittest
{
static assert(Stride!(3, 1, 2, 3, 4, 5, 6, 7, 8) == AliasSeq!(1, 4, 7));
static assert(Stride!(2, 1, 2, 3) == AliasSeq!(1, 3));
static assert(Stride!(2, 1, 2) == AliasSeq!(1));
static assert(Stride!(2, 1) == AliasSeq!(1));
static assert(Stride!(1, 1, 2, 3) == AliasSeq!(1, 2, 3));
static assert(is(Stride!3 == AliasSeq!()));
}
/**
* Aliases itself to $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true),
* to $(D_INLINECODE T[1]) if $(D_KEYWORD false).
*
* Params:
* cond = Template predicate.
* T = Two arguments.
*
* Returns: $(D_INLINECODE T[0]) if $(D_PARAM cond) is $(D_KEYWORD true),
* $(D_INLINECODE T[1]) otherwise.
*/
template Select(bool cond, T...)
if (T.length == 2)
{
static if (condition)
{
alias Select = L[0];
}
else
{
alias Select = L[1];
}
}