123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
/**
 * The traits module defines tools useful for obtaining detailed compile-time
 * information about a type.  Please note that the mixed naming scheme used in
 * this module is intentional.  Templates which evaluate to a type follow the
 * naming convention used for types, and templates which evaluate to a value
 * follow the naming convention used for functions.
 *
 * Copyright: Copyright (C) 2005-2006 Sean Kelly.  All rights reserved.
 * License:   BSD style: $(LICENSE)
 * Authors:   Sean Kelly, Fawzi Mohamed, Abscissa
 */
module tango.core.Traits;

/**
 * Strips the qualifiers from a type
 */

template BaseTypeOf( T )
{
    static if (is(T S : shared(S)))
        alias S BaseTypeOf;
    else static if (is(T S : shared(const(S))))
        alias S BaseTypeOf;
    else static if (is(T S : const(S)))
        alias S BaseTypeOf;
    else
        alias T BaseTypeOf;
}

/**
 * Computes the effective type that inout would have if you have it two parameters of difference constness
 */

template InoutTypeOf(T, M)
{
    static assert(is(BaseTypeOf!(T) == BaseTypeOf!(M)));
    static if (is(immutable(BaseTypeOf!(T)) == T) && is(immutable(BaseTypeOf!(T)) == M))
        alias immutable(BaseTypeOf!(T)) InoutTypeOf;
    else static if ((is(BaseTypeOf!(T) == T) && is(BaseTypeOf!(T) == M)))
        alias BaseTypeOf!(T) InoutTypeOf;
    else
        alias const(BaseTypeOf!(T)) InoutTypeOf;
}

/**
 * Evaluates to true if T is char[], wchar[], or dchar[].
 */
template isStringType( T )
{
    const bool isStringType = is( T : const(char)[] )  ||
                              is( T : const(wchar)[] ) ||
                              is( T : const(dchar)[] );
}

/**
 * Evaluates to true if T is char, wchar, or dchar.
 */
template isCharType( T )
{
    const bool isCharType = is( BaseTypeOf!(T) == char )  ||
                            is( BaseTypeOf!(T) == wchar ) ||
                            is( BaseTypeOf!(T) == dchar );
}


/**
 * Evaluates to true if T is a signed integer type.
 */
template isSignedIntegerType( T )
{
    const bool isSignedIntegerType = is( BaseTypeOf!(T) == byte )  ||
                                     is( BaseTypeOf!(T) == short ) ||
                                     is( BaseTypeOf!(T) == int )   ||
                                     is( BaseTypeOf!(T) == long )/+||
                                     is( T == cent  )+/;
}


/**
 * Evaluates to true if T is an unsigned integer type.
 */
template isUnsignedIntegerType( T )
{
    const bool isUnsignedIntegerType = is( BaseTypeOf!(T) == ubyte )  ||
                                       is( BaseTypeOf!(T) == ushort ) ||
                                       is( BaseTypeOf!(T) == uint )   ||
                                       is( BaseTypeOf!(T) == ulong )/+||
                                       is( T == ucent  )+/;
}


/**
 * Evaluates to true if T is a signed or unsigned integer type.
 */
template isIntegerType( T )
{
    const bool isIntegerType = isSignedIntegerType!(T) ||
                               isUnsignedIntegerType!(T);
}


/**
 * Evaluates to true if T is a real floating-point type.
 */
template isRealType( T )
{
    const bool isRealType = is( BaseTypeOf!(T) == float )  ||
                            is( BaseTypeOf!(T) == double ) ||
                            is( BaseTypeOf!(T) == real );
}


/**
 * Evaluates to true if T is a complex floating-point type.
 */
template isComplexType( T )
{
    const bool isComplexType = is( BaseTypeOf!(T) == cfloat )  ||
                               is( BaseTypeOf!(T) == cdouble ) ||
                               is( BaseTypeOf!(T) == creal );
}


/**
 * Evaluates to true if T is an imaginary floating-point type.
 */
template isImaginaryType( T )
{
    const bool isImaginaryType = is( T == ifloat )  ||
                                 is( T == idouble ) ||
                                 is( T == ireal );
}


/**
 * Evaluates to true if T is any floating-point type: real, complex, or
 * imaginary.
 */
template isFloatingPointType( T )
{
    const bool isFloatingPointType = isRealType!(T)    ||
                                     isComplexType!(T) ||
                                     isImaginaryType!(T);
}

/// true if T is an atomic type
template isAtomicType(T)
{
    static if( is( T == bool )
            || is( T == char )
            || is( T == wchar )
            || is( T == dchar )
            || is( T == byte )
            || is( T == short )
            || is( T == int )
            || is( T == long )
            || is( T == ubyte )
            || is( T == ushort )
            || is( T == uint )
            || is( T == ulong )
            || is( T == float )
            || is( T == double )
            || is( T == real )
            || is( T == ifloat )
            || is( T == idouble )
            || is( T == ireal ) )
        const isAtomicType = true;
    else
        const isAtomicType = false;
}

/**
 * complex type for the given type
 */
template ComplexTypeOf(T){
    static if(is(T==float)||is(T==ifloat)||is(T==cfloat)){
        alias cfloat ComplexTypeOf;
    } else static if(is(T==double)|| is(T==idouble)|| is(T==cdouble)){
        alias cdouble ComplexTypeOf;
    } else static if(is(T==real)|| is(T==ireal)|| is(T==creal)){
        alias creal ComplexTypeOf;
    } else static assert(0,"unsupported type in ComplexTypeOf "~T.stringof);
}

/**
 * real type for the given type
 */
template RealTypeOf(T){
    static if(is(T==float)|| is(T==ifloat)|| is(T==cfloat)){
        alias float RealTypeOf;
    } else static if(is(T==double)|| is(T==idouble)|| is(T==cdouble)){
        alias double RealTypeOf;
    } else static if(is(T==real)|| is(T==ireal)|| is(T==creal)){
        alias real RealTypeOf;
    } else static assert(0,"unsupported type in RealTypeOf "~T.stringof);
}

/**
 * imaginary type for the given type
 */
template ImaginaryTypeOf(T){
    static if(is(T==float)|| is(T==ifloat)|| is(T==cfloat)){
        alias ifloat ImaginaryTypeOf;
    } else static if(is(T==double)|| is(T==idouble)|| is(T==cdouble)){
        alias idouble ImaginaryTypeOf;
    } else static if(is(T==real)|| is(T==ireal)|| is(T==creal)){
        alias ireal ImaginaryTypeOf;
    } else static assert(0,"unsupported type in ImaginaryTypeOf "~T.stringof);
}

/// type with maximum precision
template MaxPrecTypeOf(T){
    static if (isComplexType!(T)){
        alias creal MaxPrecTypeOf;
    } else static if (isImaginaryType!(T)){
        alias ireal MaxPrecTypeOf;
    } else {
        alias real MaxPrecTypeOf;
    }
}


/**
 * Evaluates to true if T is a pointer type.
 */
template isPointerType(T)
{
        const isPointerType = false;
}

template isPointerType(T : T*)
{
        const isPointerType = true;
}

debug( UnitTest )
{
    unittest
    {
        static assert( is(BaseTypeOf!(const(int))==int) );
        static assert( is(BaseTypeOf!(immutable(int))==int) );
        static assert( is(BaseTypeOf!(shared(int))==int) );
        static assert( is(BaseTypeOf!(inout(int))==int) );
        static assert( isPointerType!(void*) );
        static assert( !isPointerType!(char[]) );
        static assert( isPointerType!(char[]*) );
        static assert( !isPointerType!(char*[]) );
        static assert( isPointerType!(real*) );
        static assert( !isPointerType!(uint) );
        static assert( is(MaxPrecTypeOf!(float)==real));
        static assert( is(MaxPrecTypeOf!(cfloat)==creal));
        static assert( is(MaxPrecTypeOf!(ifloat)==ireal));

        class Ham
        {
            void* a;
        }

        static assert( !isPointerType!(Ham) );

        union Eggs
        {
            void* a;
            uint  b;
        }

        static assert( !isPointerType!(Eggs) );
        static assert( isPointerType!(Eggs*) );

        struct Bacon {}

        static assert( !isPointerType!(Bacon) );

    }
}

/**
 * Evaluates to true if T is a a pointer, class, interface, or delegate.
 */
template isReferenceType( T )
{

    const bool isReferenceType = isPointerType!(T)  ||
                               is( T == class )     ||
                               is( T == interface ) ||
                               is( T == delegate );
}


/**
 * Evaulates to true if T is a dynamic array type.
 */
template isDynamicArrayType( T )
{
    const bool isDynamicArrayType = is( typeof(T.init[0])[] == T );
}

/**
 * Evaluates to true if T is a static array type.
 */
template isStaticArrayType( T : T[U], size_t U )
{
    const bool isStaticArrayType = true;
}

template isStaticArrayType( T )
{
    const bool isStaticArrayType = false;
}

/// true for array types
template isArrayType(T)
{
    static if (is( T U : U[] ))
        const bool isArrayType=true;
    else
        const bool isArrayType=false;
}

debug( UnitTest )
{
    unittest
    {
        static assert( isStaticArrayType!(char[5][2]) );
        static assert( !isDynamicArrayType!(char[5][2]) );
        static assert( isArrayType!(char[5][2]) );

        static assert( isStaticArrayType!(char[15]) );
        static assert( !isStaticArrayType!(char[]) );

        static assert( isDynamicArrayType!(char[]) );
        static assert( !isDynamicArrayType!(char[15]) );

        static assert( isArrayType!(char[15]) );
        static assert( isArrayType!(char[]) );
        static assert( !isArrayType!(char) );
    }
}

/**
 * Evaluates to true if T is an associative array type.
 */
template isAssocArrayType( T )
{
    const bool isAssocArrayType = is( typeof(T.init.values[0])[typeof(T.init.keys[0])] == T );
}


/**
 * Evaluates to true if T is a function, function pointer, delegate, or
 * callable object.
 */
template isCallableType( T )
{
    const bool isCallableType = is( T == function )             ||
                                is( typeof(*T) == function )    ||
                                is( T == delegate )             ||
                                is( typeof(T.opCall) == function );
}


/**
 * Evaluates to the return type of Fn.  Fn is required to be a callable type.
 */
template ReturnTypeOf( Fn )
{
    static if( is( Fn Ret == return ) )
        alias Ret ReturnTypeOf;
    else
        static assert( false, "Argument has no return type." );
}

/** 
 * Returns the type that a T would evaluate to in an expression.
 * Expr is not required to be a callable type
 */ 
template ExprTypeOf( Expr )
{
    static if(isCallableType!( Expr ))
        alias ReturnTypeOf!( Expr ) ExprTypeOf;
    else
        alias Expr ExprTypeOf;
}


/**
 * Evaluates to the return type of fn.  fn is required to be callable.
 */
template ReturnTypeOf( alias fn )
{
//    static if( is( typeof(fn) Base == typedef ) )
//        alias ReturnTypeOf!(Base) ReturnTypeOf;
//    else
        alias ReturnTypeOf!(typeof(fn)) ReturnTypeOf;
}


/**
 * Evaluates to a tuple representing the parameters of Fn.  Fn is required to
 * be a callable type.
 */
template ParameterTupleOf( Fn )
{
    static if( is( Fn Params == function ) )
        alias Params ParameterTupleOf;
    else static if( is( Fn Params == delegate ) )
        alias ParameterTupleOf!(Params) ParameterTupleOf;
    else static if( is( Fn Params == Params* ) )
        alias ParameterTupleOf!(Params) ParameterTupleOf;
    else
        static assert( false, "Argument has no parameters." );
}


/**
 * Evaluates to a tuple representing the parameters of fn.  n is required to
 * be callable.
 */
template ParameterTupleOf( alias fn )
{
//    static if( is( typeof(fn) Base == typedef ) )
//        alias ParameterTupleOf!(Base) ParameterTupleOf;
//    else
        alias ParameterTupleOf!(typeof(fn)) ParameterTupleOf;
}


/**
 * Evaluates to a tuple representing the ancestors of T.  T is required to be
 * a class or interface type.
 */
template BaseTypeTupleOf( T )
{
    static if( is( T Base == super ) )
        alias Base BaseTypeTupleOf;
    else
        static assert( false, "Argument is not a class or interface." );
}

/**
 * Strips the []'s off of a type.
 */
template BaseTypeOfArrays(T)
{
    static if( is( T S : S[]) ) {
        alias BaseTypeOfArrays!(S)  BaseTypeOfArrays;
    }
    else {
        alias T BaseTypeOfArrays;
    }
}

/**
 * strips one [] off a type
 */
template ElementTypeOfArray(T:T[])
{
    alias T ElementTypeOfArray;
}

/**
 * Count the []'s on an array type
 */
template rankOfArray(T) {
    static if(is(T S : S[])) {
        const uint rankOfArray = 1 + rankOfArray!(S);
    } else {
        const uint rankOfArray = 0;
    }
}

/// type of the keys of an AA
template KeyTypeOfAA(T){
    alias typeof(T.init.keys[0]) KeyTypeOfAA;
}

/// type of the values of an AA
template ValTypeOfAA(T){
    alias typeof(T.init.values[0]) ValTypeOfAA;
}

/// returns the size of a static array
template staticArraySize(T)
{
    static assert(isStaticArrayType!(T),"staticArraySize needs a static array as type");
    static assert(rankOfArray!(T)==1,"implemented only for 1d arrays...");
    const size_t staticArraySize=(T).sizeof / typeof(*T.ptr).sizeof;
}

/// is T is static array returns a dynamic array, otherwise returns T
template DynamicArrayType(T)
{
    static if( isStaticArrayType!(T) )
        alias typeof(T.dup) DynamicArrayType;
    else
        alias T DynamicArrayType;
}

debug( UnitTest )
{
    static assert( is(BaseTypeOfArrays!(real[][])==real) );
    static assert( is(BaseTypeOfArrays!(real[2][3])==real) );
    static assert( is(ElementTypeOfArray!(real[])==real) );
    static assert( is(ElementTypeOfArray!(real[][])==real[]) );
    static assert( is(ElementTypeOfArray!(real[2][])==real[2]) );
    static assert( is(ElementTypeOfArray!(real[2][2])==real[2]) );
    static assert( rankOfArray!(real[][])==2 );
    static assert( rankOfArray!(real[2][])==2 );
    static assert( is(ValTypeOfAA!(char[int])==char));
    static assert( is(KeyTypeOfAA!(char[int])==int));
    static assert( is(ValTypeOfAA!(char[][int])==char[]));
    static assert( is(KeyTypeOfAA!(char[][int[]])==const(int)[]));
    static assert( isAssocArrayType!(char[][int[]]));
    static assert( !isAssocArrayType!(char[]));
    static assert( is(DynamicArrayType!(char[2])==DynamicArrayType!(char[])));
    static assert( is(DynamicArrayType!(char[2])==char[]));
    static assert( staticArraySize!(char[2])==2);
}

// ------- CTFE -------

/// compile time integer to string
char [] ctfe_i2a(int i){
    char[] digit="0123456789".dup;
    char[] res="".dup;
    if (i==0){
        return "0".dup;
    }
    bool neg=false;
    if (i<0){
        neg=true;
        i=-i;
    }
    while (i>0) {
        res=digit[i%10]~res;
        i/=10;
    }
    if (neg)
        return '-'~res;
    else
        return res;
}
/// ditto
char [] ctfe_i2a(long i){
    char[] digit="0123456789".dup;
    char[] res="".dup;
    if (i==0){
        return "0".dup;
    }
    bool neg=false;
    if (i<0){
        neg=true;
        i=-i;
    }
    while (i>0) {
        res=digit[cast(size_t)(i%10)]~res;
        i/=10;
    }
    if (neg)
        return '-'~res;
    else
        return res;
}
/// ditto
char [] ctfe_i2a(uint i){
    const(char)[] digit="0123456789";
    char[] res;
    if (i==0){
        return "0".dup;
    }
    bool neg=false;
    while (i>0) {
        res=digit[i%10]~res;
        i/=10;
    }
    return res;
}
/// ditto
char [] ctfe_i2a(ulong i){
    const(char)[] digit="0123456789";
    char[] res;
    if (i==0){
        return "0".dup;
    }
    bool neg=false;
    while (i>0) {
        res=digit[cast(size_t)(i%10)]~res;
        i/=10;
    }
    return res;
}

debug( UnitTest )
{
    unittest {
    static assert( ctfe_i2a(31)=="31" );
    static assert( ctfe_i2a(-31)=="-31" );
    static assert( ctfe_i2a(14u)=="14" );
    static assert( ctfe_i2a(14L)=="14" );
    static assert( ctfe_i2a(14UL)=="14" );
    }
}