#include "main.h"

#include <exception>  // std::exception

struct Foo {
  static Index object_count;
  static Index object_limit;
  int dummy;

  Foo() : dummy(0) {
#ifdef EIGEN_EXCEPTIONS
    // TODO: Is this the correct way to handle this?
    if (Foo::object_count > Foo::object_limit) {
      std::cout << "\nThrow!\n";
      throw Foo::Fail();
    }
#endif
    std::cout << '+';
    ++Foo::object_count;
  }

  ~Foo() {
    std::cout << '-';
    --Foo::object_count;
  }

  class Fail : public std::exception {};
};

Index Foo::object_count = 0;
Index Foo::object_limit = 0;

#undef EIGEN_TEST_MAX_SIZE
#define EIGEN_TEST_MAX_SIZE 3

EIGEN_DECLARE_TEST(ctorleak) {
  typedef Matrix<Foo, Dynamic, Dynamic> MatrixX;
  typedef Matrix<Foo, Dynamic, 1> VectorX;

  Foo::object_count = 0;
  for (int i = 0; i < g_repeat; i++) {
    Index rows = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE),
          cols = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE);
    Foo::object_limit = rows * cols;
    {
      MatrixX r(rows, cols);
      Foo::object_limit = r.size() + internal::random<Index>(0, rows * cols - 2);
      std::cout << "object_limit =" << Foo::object_limit << std::endl;
#ifdef EIGEN_EXCEPTIONS
      try {
#endif
        if (internal::random<bool>()) {
          std::cout << "\nMatrixX m(" << rows << ", " << cols << ");\n";
          MatrixX m(rows, cols);
        } else {
          std::cout << "\nMatrixX m(r);\n";
          MatrixX m(r);
        }
#ifdef EIGEN_EXCEPTIONS
        VERIFY(false);             // not reached if exceptions are enabled
      } catch (const Foo::Fail&) { /* ignore */
      }
#endif
    }
    VERIFY_IS_EQUAL(Index(0), Foo::object_count);

    {
      Foo::object_limit = (rows + 1) * (cols + 1);
      MatrixX A(rows, cols);
      VERIFY_IS_EQUAL(Foo::object_count, rows * cols);
      VectorX v = A.row(0);
      VERIFY_IS_EQUAL(Foo::object_count, (rows + 1) * cols);
      v = A.col(0);
      VERIFY_IS_EQUAL(Foo::object_count, rows * (cols + 1));
    }
    VERIFY_IS_EQUAL(Index(0), Foo::object_count);
  }
  std::cout << "\n";
}