/*
TEST_OUTPUT:
---
This actually gets evaluated!
()
(bool)
(bool, short)
(bool, short, int)
Alias Test instantiated
Alias Test instantiated
---
*/
import core.stdc.stdio;

/*********************************************************/

template Foo(T)
{
    static if ( is(T : int) )
        alias T t1;

    static if (T.sizeof == 4)
        alias T t2;

    static if ( is(T AB : int) )
        alias AB t3;

    static if ( is(T* V : V*) )
        alias V t4;

    static if ( is(T W) )
        alias W t5;
    else
        alias char t5;

    static if ( is(T* X : X*) )
    {
    }
}

void test1()
{
    Foo!(int).t1 x1;
    assert(typeid(typeof(x1)) == typeid(int));

    Foo!(int).t2 x2;
    assert(typeid(typeof(x2)) == typeid(int));

    Foo!(int).t3 x3;
    assert(typeid(typeof(x3)) == typeid(int));

    Foo!(int).t4 x4;
    assert(typeid(typeof(x4)) == typeid(int));

    Foo!(int).t5 x5;
    assert(typeid(typeof(x5)) == typeid(int));

    Foo!(int).X x6;
    assert(typeid(typeof(x6)) == typeid(int));
}

/*********************************************************/


void test2()
{
    alias int T;

    static if ( is(T : int) )
        alias T t1;

    static if (T.sizeof == 4)
        alias T t2;

    static if ( is(T U : int) )
        alias U t3;

    static if ( is(T* V : V*) )
        alias V t4;

    static if ( is(T W) )
        alias W t5;
    else
        alias char t5;

    static if ( is(T* X : X*) )
    {
    }

    t1 x1;
    assert(typeid(typeof(x1)) == typeid(int));

    t2 x2;
    assert(typeid(typeof(x2)) == typeid(int));

    t3 x3;
    assert(typeid(typeof(x3)) == typeid(int));

    t4 x4;
    assert(typeid(typeof(x4)) == typeid(int));

    t5 x5;
    assert(typeid(typeof(x5)) == typeid(int));

    X x6;
    assert(typeid(typeof(x6)) == typeid(int));
}

/*********************************************************/

void test3()
{
    static if ( is(short : int) )
    {
        printf("1\n");
    }
    else
        assert(0);
    static if ( is(short == int) )
        assert(0);
    static if ( is(int == int) )
    {
        printf("3\n");
    }
    else
        assert(0);
}

/*********************************************************/

void test4()
{
    alias void Function(int);

    static if (is(Function Void == function))
        printf("if\n");
    else
        assert(0);

//    static if (is(Void == void))
//        printf("if\n");
//    else
//        assert(0);


    alias byte delegate(int) Delegate;

    static if (is(Delegate Foo == delegate))
        printf("if\n");
    else
        assert(0);

    static if (is(Foo Byte == function))
        printf("if\n");
    else
        assert(0);

//    static if (is(Byte == byte))
//        printf("if\n");
//    else
//        assert(0);


    union Union { }

    static if (is(Union == union))
        printf("if\n");
    else
        assert(0);


    struct Struct { }

    static if (is(Struct == struct))
        printf("if\n");
    else
        assert(0);


    enum Enum : short { EnumMember }

    static if (is(Enum Short == enum))
        printf("if\n");
    else
        assert(0);

    static if (is(Short == short))
        printf("if\n");
    else
        assert(0);

    class Class { }

    static if (is(Class == class))
        printf("if\n");
    else
        assert(0);


    interface Interface { }

    static if (is(Interface == interface))
        printf("if\n");
    else
        assert(0);

}

/*********************************************************/

class Foo5(T)
{
    Node sentinel;

    struct Node
    {
        int value;
    }
}

void test5()
{
    Foo5!(int) bar=new Foo5!(int);
    bar.sentinel.value = 7;
}


/*********************************************************/

template factorial6(int n)
{
    static if (n == 1)
        const int factorial6 = 1;
    else
        const int factorial6 = n * .factorial6!(n-1);
}

void test6()
{
    int i = factorial6!(4);
    printf("%d\n", i);
    assert(i == 24);
}


/*********************************************************/

template whale(string walrus)
{
    const char [] whale = walrus;
}

template dolphin(string fish)
{
   const char [] dolphin = whale!(fish[0..3]);
}

const char [] urchin1 = dolphin!("anenome");
const char [] urchin2 = whale!("anenome"[0..3]);

template dolphin3(string fish)
{
   const char [] dolphin3 = fish[0..3];
}

const char [] urchin3 = dolphin3!("anenome");

template dolphin4(string fish)
{
  const char [] dolphin4 = whale!(fish[0..(3)]);
}

const char [] urchin4 = dolphin4!("anenome");

template dolphin5(string fish)
{
  const char [] dolphin5 = whale!(fish[(0)..3]);
}

const char [] urchin5 = dolphin5!("anenome");

void test8()
{
    assert(urchin1 == "ane");
    assert(urchin2 == "ane");
    assert(urchin3 == "ane");
    assert(urchin4 == "ane");
    assert(urchin5 == "ane");
}

/*********************************************************/

int testEmpty(string s) { return 0; }

template Recurse(string pattern){
}


template slice(string str, int from, int to)
{
    const string slice = str[from..to];
}


template Compile(string pattern)
{
    const string left = slice!(pattern,4,pattern.length);

    const string remaining = slice!(left,1,left.length);

    alias Recurse!(remaining) fn;
}



template Match(string pattern)
{
    alias Compile!(pattern) Match;
}

void test9()
{
    alias Match!("abcdefghijk") f;
}

/*********************************************************/

template Foo10(string s)
{
    const string Foo10 = s;
}

void test10()
{
    string s;

    s = Foo10!("abc" ~ "e");
    assert(s == "abce");

    s = Foo10!("abc" ~ 'f');
    assert(s == "abcf");

    s = Foo10!('g' ~ "abc");
    assert(s == "gabc");

    s = Foo10!('g' ~ "abc" ~ 'h');
    assert(s == "gabch");
}

/*********************************************************/

template Foo11(string s)
{
    const string Foo11 = s;
}

void test11()
{
    string s;

    s = Foo11!("abcdef"[1..$ - 1]);
    assert(s == "bcde");
}

/*********************************************************/

template Foo12(int i)
{
    const int Foo12 = i;
}

void test12()
{
    int i;

    i = Foo12!("abcdef" == "abcdef");
    assert(i == 1);
    i = Foo12!("abcdef" == "abcqef");
    assert(i == 0);
    i = Foo12!("abcdef" == "abc");
    assert(i == 0);
    i = Foo12!("abc" == "abcdef");
    assert(i == 0);
}

/*********************************************************/

const a13 = 3;
static if (a13 == 3)
    int b13 = 7;

template Foo13(int i)
{
    const int j = i + 1;
    static if (j == 3)
        const int k = 2;
}

void test13()
{
    assert(b13 == 7);
    assert(Foo13!(2).k == 2);
}

/*********************************************************/

template zebra(string w)
{
    static if (w.length > 2 && w[1] == 'q')
        const bool zebra = true;
    else
        const bool zebra = false;
}

template horse(string w)
{
    static if (w.length == 1 || w[1] == 'q')
        const bool horse = true;
    else
        const bool horse = false;
}

void test14()
{
    bool lion = zebra!("a");
    assert(!lion);
    lion = zebra!("aqb");
    assert(lion);

    lion = horse!("a");
    assert(lion);
    lion = horse!("aqb");
    assert(lion);
    lion = horse!("ab");
    assert(!lion);
}

/*********************************************************/

template factorial15(int n)
{
   static if (n<2) const int factorial15 = 1;
   else const int factorial15 = n * factorial15!(n-1);
}

template rhino15(alias hippo)
{
   const int rhino15 = hippo!(3);
}

void test15()
{
    const int lion = rhino15!(factorial15);
    assert(lion == 6);
}

/*********************************************************/

// Create a constant array of int or uint sized items
// as a dstring string. n is the index of the last item.
template makeLookup(alias entry, int n)
{
   static if (n == -1) // start with an empty array...
     const dchar [] makeLookup = "";
   else        // ... and fill it up
     const dchar [] makeLookup = makeLookup!(entry, n-1)
           ~ cast(dchar)entry!(n);
}

template factorial16(uint n)
{
     static if (n<2) const uint factorial16 = 1;
     else const factorial16 = n * factorial16!(n-1);
}

// Make an array of factorials from 0 to 13 (14!> uint.max)
const smallfactorials = makeLookup!(factorial16, 13);

const uint[14] testtable =
[
  1,
  1,
  2,
  6,
  24,
  120,
  720,
  5040,
  40320,
  362880,
  3628800,
  39916800,
  479001600,
  1932053504,
];

void test16()
{
    for (int i=0; i<smallfactorials.length; ++i)
    {
        printf("%d  %d\n", i, smallfactorials[i]);
        assert(smallfactorials[i] == testtable[i]);
    }
}

/*********************************************************/

template dingo(int a)
{
    pragma(msg, "This actually gets evaluated!");
    static if (a==2) {
        const int dingo = 28;
    }
    else
        const int dingo = a;
}

const int bilby = dingo!(2);

void test17()
{
    assert(bilby == 28);
}

/*********************************************************/

template frog(char F)
{
     const int frog = 1;
}

template frog(int F)
{
     const int frog = 2;
}

template frog(char F: 'A')
{
     const int frog = 3;
}

template frog(int F: 65)
{
     const int frog = 4;
}

static assert( frog!('B')==1);
static assert( frog!(64)==2);
static assert( frog!('A')==3);
static assert( frog!(65)==4);

void test18()
{
}

/*********************************************************/

void test19()
{
    MakeTuple19!(int,int) t;
    assert(t.sizeof == int.sizeof);
}

template MakeTuple19( T1, T2 )
{
     alias int MakeTuple19;
}

template MakeTuple19(T1, T2, T3=float)
{
     alias long MakeTuple19;
}


/*********************************************************/

template sqrt(real x, real root = x/2, int ntries = 0)
{
  static if (ntries == 5)
    // precision doubles with each iteration,
    // 5 should be enough
    const sqrt = root;
  else static if (root * root - x == 0)
    const sqrt = root;  // exact match
  else
    // iterate again
    const sqrt = sqrt!(x, (root+x/root)/2, ntries+1);
}

void test20()
{
    real x = sqrt!(2);
    printf("%.20Lg\n", x); // 1.4142135623730950487
}

/*********************************************************/

template hash(string s, uint sofar=0)
{
   static if (s.length == 0)
      const hash = sofar;
   else
      const hash = hash!(s[1 .. $], sofar * 11 + s[0]);
}

uint foo21()
{
    return hash!("hello world");
}

void test21()
{
    auto i = foo21();
    printf("%d\n", i);
    assert(i == 1871483972);
}


/*********************************************************/

T Foo22(T)(int i)
{
    printf("i = %d\n", i);
    return cast(T)0;
}

void test22()
{
    Foo22!(int)(3);
}

/*********************************************************/

template fish( char s)
{
  const bool fish = true;
}

template dog(string bird)
{
        static if (bird.length>99 && fish!( (bird[95])) )
           const int dog = 2;
        else const int dog = 3;
}

const int pig = dog!("a");

void test23()
{
}

/*********************************************************/

T delegate (T) acc24 (T) (T n)
{
    return (T i) { return n += i; };
}

void test24()
{
    auto acc1 = acc24 (4);
}

/*********************************************************/

T func25(T, T c = 1)(T x)
{
    return x * c;
}

void test25()
{
    double d;

    d = func25(1.0);
    assert(d == 1.0);

    d = func25(2.0);
    assert(d == 2.0);

    d = func25!(double)(2.0);
    assert(d == 2.0);

    d = func25!(double, 3)(2.0);
    assert(d == 6.0);
}

/*********************************************************/

class Foo26 {}
class Bar26 {}

string name26;

template aliastest(alias A) {
    pragma(msg,"Alias Test instantiated");
    void aliastest() {
        name26 = (new A!().al).classinfo.name;
        //writefln("Alias Test: ", name26);
    }
}

template boxtpl(alias A) {
    template box() {
        alias A al;
    }
}

void test26()
{
    aliastest!(boxtpl!(Foo26).box) ();
    assert(name26 == "template4.Foo26");
    aliastest!(boxtpl!(Bar26).box) ();
    assert(name26 == "template4.Bar26");
}

/*********************************************************/

struct TFoo27(int x) { }
alias TFoo27!(3) a;
alias TFoo27!(2+1) b;
alias TFoo27!(3u) c;

static assert(is(TFoo27!(3) == TFoo27!(2 + 1)));
static assert(is(TFoo27!(3) == TFoo27!(3u)));

void test27()
{
}

/*********************************************************/

struct SiQuantity
{
    real value = 0;
    static assert(SiQuantity.sizeof == real.sizeof);

    template AddDimensions(int mul, U) { }
}

void test28()
{
}

/*********************************************************/

template Count29() { const Count29 = 5; }

void test29()
{
    int[Count29!()] x;

    assert(x.length == 5);
}

/*********************************************************/

class FooClass(T) {  T data; }

struct FooStruct(T) {  T data;  }

void bar_struct(T)(FooStruct!(T) a) {}

void bar_class(T)(FooClass!(T) a) {}

void test30()
{
    auto C = new FooClass!(double);
    FooStruct!(double) S;

    bar_struct(S);
    bar_class!(double)(C);
    bar_class(C);
}

/*********************************************************/

V get31(V,K)(V[K] dict, K key, V def = V.init)
{
    V* ptr = key in dict;
    return ptr? *ptr: def;
}


string get31x(string[int] dict, int key, string def = null)
{
    string* ptr = key in dict;
    return ptr? *ptr: def;
}


void test31()
{
    string[int] i2s;
    i2s[1] = "Hello";
    i2s[5] = "There";

    auto result = i2s.get31(1, "yeh");
    printf("%.*s\n", cast(int)result.length, result.ptr);
    result = i2s.get31(2, "default");
    printf("%.*s\n", cast(int)result.length, result.ptr);
    result = i2s.get31(1);
    printf("%.*s\n", cast(int)result.length, result.ptr);
    result = i2s.get31(2);
    printf("%.*s\n", cast(int)result.length, result.ptr);
}

/*********************************************************/

void delegate(T, S) Concat(S, T...)(void delegate(T) one, void delegate(S) two)
{
  return( delegate void(T t, S s){} );
}

void test32()
{
  void delegate(char, char, int) wtf = Concat(
    delegate void(char a, char b) {},
    delegate void(int lol) {}
  );
}

/*********************************************************/

struct Composer(T) {
    alias T delegate(T) Fun;
    Fun[] funs;
    public T opCall()(T x) {
        T result = x;
        foreach_reverse (f; funs)
        {
            result = f(result);
        }
        return result;
    }
    public void opOpAssign(string op)(Fun f) if (op == "+") {
        funs ~= f;
    }
}

struct square(T) {
    T opCall(T t) {
        return t*t;
    }
}

struct plus1(T) {
    T opCall(T t) {
        return t+1;
    }
}

struct div3(T) {
    T opCall(T t) {
        return t/3.0;
    }
}

T delegate(T) tofp(T : S!(T), alias S)()
{
    class Foo
    {
        div3!(T) arg;

        T bar(T t)
        {
            return arg(t);
        }
    }

    Foo f = new Foo;
    return &f.bar;
}

void test33() {
    Composer!(double) comp;
    comp += delegate double (double x) { return x/3.0;};
    comp += delegate double (double x) { return x*x;};
    comp += (double x) => x + 1.0;
    printf("%f\n", comp(2.0));

    // Try function objects
    Composer!(double) comp2;
    comp2 += tofp!(div3!(double))();
    comp2 += tofp!(square!(double))();
    comp2 += tofp!(plus1!(double))();
    printf("%f\n", comp2( 2.0));
}

/*********************************************************/

template Print34(Ts ...) { pragma (msg, Ts.stringof); }

template Tuple34(Ts ...) { alias Ts Tuple34; }

template Decode34( T )                                       { alias Tuple34!() Types; }
template Decode34( T : TT!(U1),       alias TT, U1 )         { alias Tuple34!(U1) Types; }
template Decode34( T : TT!(U1,U2),    alias TT, U1, U2 )     { alias Tuple34!(U1,U2) Types; }
template Decode34( T : TT!(U1,U2,U3), alias TT, U1, U2, U3 ) { alias Tuple34!(U1,U2,U3) Types; }


struct S34_1(T1) {}
struct S34_2(T1, T2) {}
struct S34_3(T1, T2, T3) {}

alias Decode34!( bool ).Types SQ0;
alias Decode34!( S34_1!(bool) ).Types SQ1;
alias Decode34!( S34_2!(bool,short) ).Types SQ2;
alias Decode34!( S34_3!(bool,short,int) ).Types SQ3;

mixin Print34!(SQ0);
mixin Print34!(SQ1);
mixin Print34!(SQ2);
mixin Print34!(SQ3);

void test34()
{
}

/*********************************************************/

template strof(T)
{
    static string strof = T.stringof;
}

void test35()
{
    alias typeof(delegate () { return (char[]).init;} ) del;
    auto a = strof!(del);
    auto aa = strof!(int);
    auto ab = strof!(typeof(5));
    auto meth = delegate () { return (char[]).init;};
    auto b = strof!(typeof(meth));
    auto c = strof!(typeof(delegate () { return 5; } ));
    auto d = strof!(typeof(delegate () { return (char[]).init;} ));
}

/*********************************************************/

struct Number36(int N)
{
    const int value = N;
}

struct Foo36(T)
{
    int talk() { printf("Not so special:\n"); return 1; }
}

struct Foo36(T : Number36!(N), int N)
{
    int talk() { printf("Ooh special - NUMBER N\n"); return 2; }
}

void test36()
{
    Foo36!(Number36!(5)) x;
    auto i = x.talk();
    assert(i == 2);
}

/*********************************************************/

struct Base37(T) {}

alias Base37 Alias37;

void foo37(T)(Alias37!(T) x) {}

void test37()
{
   Base37!(float) b;
   foo37(b); // fails!
}

/*********************************************************/

void sort(alias dg, T)(T[] arr)
{
    bool a = dg(1,2);
    printf("a = %d\n", a);
    assert(a == true);
}

void test38()
{   int[] arr;
    int i = 3;

    sort!( (x,y){ return x + i > y; } )(arr);
    sort!( (int x,int y){ return x + i > y; } )(arr);
}

/*********************************************************/

void bug4652(U, T...)(long y, T x, U num) {}
void bug4652default(T) (T value, int x=2) {}
void bug4652default(T) (T value, int y) {}
void bug4676(T...)(T args, string str) {}
void bug4676(T...)(T args) {}

void instantiate4652()
{
    bug4652(2, 'c', 27, 'e', 'f',1); // rejects-valid
    bug4652(2, 1);  // infinite loop on valid code
    bug4652default(true);
    bug4676(1, 2, 3);
}

/*********************************************************/
// https://issues.dlang.org/show_bug.cgi?id=7589

struct T7589(T)
{
    void n;
}
static assert(!__traits(compiles, T7589!(int)));

int bug7589b(T)() @safe { int *p; *(p + 8) = 6; }
static assert(!__traits(compiles, bug7589b!(int)()+7 ));


/*********************************************************/

int bar39(alias dg)(int i)
{
    return dg(i);
}

void test39()
{
    auto i = bar39!(a => a + 1)(3);
    if (i != 4)
        assert(0);
}

/*********************************************************/
// https://issues.dlang.org/show_bug.cgi?id=6701

uint foo_6701(uint v:0)() { return 1; }
uint foo_6701(uint v)() { return 0; }
uint foo2_6701(uint v:0, string op)() { return 1; }
uint foo2_6701(uint v, string op)() { return 0; }

void test6701()
{
    assert(foo_6701!(0u)() == 1);
    assert(foo2_6701!(0u, "+")() == 1);
}

/******************************************/
// https://issues.dlang.org/show_bug.cgi?id=7469

struct Foo7469a(int x) { }
struct Foo7469b(int x) { }
struct Foo7469c(alias v) { }
struct Foo7469d(T...) { }
struct Foo7469e(int a, T...) { }
struct Foo7469f(T, int k=1) { }
struct Foo7469g(T, int k=1) { }
struct Foo7469h(uint x) { }

import core.demangle : demangleType;

void test7469()
{
    static assert(demangleType(Foo7469a!(3 )    .mangleof) == "template4.Foo7469a!(3).Foo7469a");
    static assert(demangleType(Foo7469a!(3u)    .mangleof) == "template4.Foo7469a!(3).Foo7469a");
    static assert(demangleType(Foo7469b!(3u)    .mangleof) == "template4.Foo7469b!(3).Foo7469b");
    static assert(demangleType(Foo7469b!(3 )    .mangleof) == "template4.Foo7469b!(3).Foo7469b");
    static assert(demangleType(Foo7469c!(3 )    .mangleof) == "template4.Foo7469c!(3).Foo7469c");
    static assert(demangleType(Foo7469c!(3u)    .mangleof) == "template4.Foo7469c!(3u).Foo7469c");
    static assert(demangleType(Foo7469d!(3 )    .mangleof) == "template4.Foo7469d!(3).Foo7469d");
    static assert(demangleType(Foo7469d!(3u)    .mangleof) == "template4.Foo7469d!(3u).Foo7469d");
    static assert(demangleType(Foo7469e!(3u, 5u).mangleof) == "template4.Foo7469e!(3, 5u).Foo7469e");
    static assert(demangleType(Foo7469f!(int, 1).mangleof) == "template4.Foo7469f!(int, 1).Foo7469f");
    static assert(demangleType(Foo7469f!(int)   .mangleof) == "template4.Foo7469f!(int, 1).Foo7469f");
    static assert(demangleType(Foo7469g!(int)   .mangleof) == "template4.Foo7469g!(int, 1).Foo7469g");
    static assert(demangleType(Foo7469g!(int, 1).mangleof) == "template4.Foo7469g!(int, 1).Foo7469g");
    static assert(demangleType(Foo7469h!(3 )    .mangleof) == "template4.Foo7469h!(3u).Foo7469h");
    static assert(demangleType(Foo7469h!(3u)    .mangleof) == "template4.Foo7469h!(3u).Foo7469h");
}

/******************************************/

template foo7698a(T, T val : 0)
{
    enum foo7698a = val;
}

T foo7698b(T, T val : 0)()
{
    return val;
}

T foo7698c(T, T val : T.init)()
{
    return val;
}

void test7698()
{
    static assert(foo7698a!(int, 0) == 0);
    assert(foo7698b!(int, 0)() == 0);
    assert(foo7698c!(int, 0)() == 0);
}

/*********************************************************/

int main()
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test8();
    test9();
    test10();
    test11();
    test12();
    test13();
    test14();
    test15();
    test16();
    test17();
    test18();
    test19();
    test20();
    test21();
    test22();
    test23();
    test24();
    test25();
    test26();
    test27();
    test28();
    test29();
    test30();
    test31();
    test32();
    test33();
    test34();
    test35();
    test36();
    test37();
    test38();
    test39();
    test6701();
    test7698();

    printf("Success\n");
    return 0;
}
