// RUN: clang-cc -fsyntax-only -verify %s
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>

// Placement new requires <new> to be included, but we don't support that yet.
void* operator new(size_t, void* ptr) throw() {
  return ptr;
}
void operator delete(void*, void*) throw() {
}

template<typename T>
class dynarray {
public:
  dynarray() { Start = Last = End = 0; }

  dynarray(const dynarray &other) {
    Start = (T*)malloc(sizeof(T) * other.size());
    Last = End = Start + other.size();

    for (unsigned I = 0, N = other.size(); I != N; ++I)
      new (Start + I) T(other[I]);
  }
  
  ~dynarray() {
    free(Start);
  }

  dynarray &operator=(const dynarray &other) {
    T* NewStart = (T*)malloc(sizeof(T) * other.size());

    for (unsigned I = 0, N = other.size(); I != N; ++I)
      new (NewStart + I) T(other[I]);

    // FIXME: destroy everything in Start
    free(Start);
    Start = NewStart;
    Last = End = NewStart + other.size();
    return *this;
  }

  unsigned size() const { return Last - Start; }
  unsigned capacity() const { return End - Start; }

  void push_back(const T& value) {
    if (Last == End) {
      unsigned NewCapacity = capacity() * 2;
      if (NewCapacity == 0)
        NewCapacity = 4;

      T* NewStart = (T*)malloc(sizeof(T) * NewCapacity);

      unsigned Size = size();
      for (unsigned I = 0; I != Size; ++I)
        new (NewStart + I) T(Start[I]);

      // FIXME: destruct old values
      free(Start);

      Start = NewStart;
      Last = Start + Size;
      End = Start + NewCapacity;
    }

    new (Last) T(value);
    ++Last;
  }

  void pop_back() {
    // FIXME: destruct old value
    --Last;
  }

  T& operator[](unsigned Idx) {
    return Start[Idx];
  }

  const T& operator[](unsigned Idx) const {
    return Start[Idx];
  }

  typedef T* iterator;
  typedef const T* const_iterator;

  iterator begin() { return Start; }
  const_iterator begin() const { return Start; }
  
  iterator end() { return Last; }
  const_iterator end() const { return Last; }
  
public:
  T* Start, *Last, *End;
};

struct Point { 
  Point() { x = y = z = 0.0; }
  Point(const Point& other) : x(other.x), y(other.y), z(other.z) { }

  float x, y, z;
};

// FIXME: remove these when we have implicit instantiation for member
// functions of class templates.
template class dynarray<int>;
template class dynarray<Point>;

int main() {
  dynarray<int> di;
  di.push_back(0);
  di.push_back(1);
  di.push_back(2);
  di.push_back(3);
  di.push_back(4);
  assert(di.size() == 5);
  for (dynarray<int>::iterator I = di.begin(), IEnd = di.end(); I != IEnd; ++I)
    assert(*I == I - di.begin());

  for (int I = 0, N = di.size(); I != N; ++I)
    assert(di[I] == I);

  di.pop_back();
  assert(di.size() == 4);
  di.push_back(4);

  dynarray<int> di2 = di;
  assert(di2.size() == 5);
  assert(di.begin() != di2.begin());
  for (dynarray<int>::iterator I = di2.begin(), IEnd = di2.end(); 
       I != IEnd; ++I)
    assert(*I == I - di2.begin());

  dynarray<int> di3(di);
  assert(di3.size() == 5);
  assert(di.begin() != di3.begin());
  for (dynarray<int>::iterator I = di3.begin(), IEnd = di3.end(); 
       I != IEnd; ++I)
    assert(*I == I - di3.begin());

  dynarray<int> di4;
  assert(di4.size() == 0);
  di4 = di;
  assert(di4.size() == 5);
  assert(di.begin() != di4.begin());
  for (dynarray<int>::iterator I = di4.begin(), IEnd = di4.end(); 
       I != IEnd; ++I)
    assert(*I == I - di4.begin());

  return 0;
}