# 1) template definition

template foo
{
  i;
}


# 2) create instance without constructor

template foo
{
  i = 42;
}
a = new foo();

if (!is_struct(a) || a.__template != "foo" || a.i != 42) exit(2);


# 3) create instance with constructor

template foo
{
  void foo()
  {
    b = 12;
    global("b");
  }
}
a = new foo();

if (!is_struct(a) || a.__template != "foo" || b != 12) exit(3);


# 4) call method static

template foo
{
  int sense()
  {
    return 42;
  }
}
a = foo::sense();

if (a != 42) exit(4);


# 5) call method dynamic

template foo
{
  int sense()
  {
    return 42;
  }
}
a = new foo();
b = a.sense();

if (b != 42) exit(5);


# 6) method accessing struct field

template foo
{
  i = 42;

  int geti()
  {
    return this.i;
  }
}
a = new foo();
b = a.geti();

if (b != 42) exit(6);


# 7) method changing struct field

template foo
{
  i = 1;

  void double()
  {
    this.i *= 2;
  }
}
a = new foo();
a.double();

if (a.i != 2) exit(7);


# 8) constructor calling other methods

template foo
{
  i = 1;

  void double()
  {
    this.i *= 2;
  }

  void foo()
  {
    this.double();
    this.double();
  }
}
a = new foo();

if (a.i != 4) exit(8);


# 9) method calling other methods

template foo
{
  i = 1;

  void double()
  {
    this.i *= 2;
  }

  void quadruple()
  {
    this.double();
    this.double();
  }
}
a = new foo();
a.quadruple();

if (a.i != 4) exit(9);


# 10) declaration with inheritance

template foo
{
  i = 1;
}
template bar extends foo
{
  j = 2;
}


# 11) inheritance

template foo
{
  i = 1;

  void double()
  {
    this.i *= 2;
  }
}
template bar extends foo
{
  j = 0;

  void quadruple()
  {
    this.double();
    this.double();
    ++this.j;
  }
}
a = new bar();
a.quadruple();

if (a.i != 4 || a.j != 1) exit(11);


# 12) inherited constructor

template foo
{
  void foo()
  {
    this.i = 42;
  }
}
template bar extends foo
{
  int geti()
  {
    return this.i;
  }
}
a = new bar();
b = a.geti();

if (a.__template != "bar" || b != 42) exit(12);


# 13) unused inherited constructor

template foo
{
  void foo()
  {
    this.i = 42;
  }
}
template bar extends foo
{
  void bar()
  {
    this.i = 100;
  }
  int geti()
  {
    return this.i;
  }
}
a = new bar();
b = a.geti();

if (a.__template != "bar" || b != 100) exit(13);


# 14) directly read static variables

template foo
{
  x = 12;
  int scale()
  {
    return 42;
  }
}
a = foo::x;
b = foo::scale;

if (a != 12 || !is_fn(b)) exit(14);


# 15) directly initialize method

template foo
{
  x = type_of;
}
a = new foo();
b = a.x(12);
c = foo::x(12);

if (b != "int" || c != "int") exit(15);


# 16) use of template name as variable name

template foo
{
  i = 42;
}
a = foo;

if (a != ()) exit(16);


# 17) static method calling another

template foobar
{
  int bar()
  {
    return 2;
  }
  int foo()
  {
    return 2 * foobar::bar();
  }
}
a = foobar::foo();

if (a != 4) exit(17);


# 18) static method recursion

template foo
{
  int bar(int i)
  {
    if (i > 0) {
      return foo::bar(i - 1);
    } else {
      return -1;
    }
  } 
}
a = foo::bar(10);

if (a != -1) exit(18);


print("18 subtests ");
