#include "vclmat.h"
using namespace scl;
using namespace std;

class cycle {
private:
  int i;
public:
  cycle() : i(0) {}
  float operator()()  { return float(++i % 1000); }
};


int main(int argc, char** argp, char** envp)
{

  cycle fill_value;
 
  const INTEGER r = 10;
  const INTEGER c = 6;

  cout << boolalpha << '\n';

  // Check that begin and end work
  {
    realmat A(r,c,1.0);
    vclmat cpu_A(A);
    if (A.begin() == cpu_A.begin() ) {
      cout << "begin works" << '\n';
    }
    else {
      cout << "begin failed" << '\n';
    }
    if (A.end() == cpu_A.end() ) {
      cout << "end works" << '\n';
    }
    else {
      cout << "end failed" << '\n';
    }
  }

  // Check that sample code in header works
  {
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    vclmat cpu_B = cpu_A;
    realmat B = cpu_B;
    for (INTEGER j=1; j<=A.ncol(); ++j) {
      for (INTEGER i=1; i<=A.nrow(); ++i) {
        if (cpu_A(i-1,j-1) != Asave(i,j)) cout << "error #1" << '\n';
        if (cpu_B(i-1,j-1) != Asave(i,j)) cout << "error #2" << '\n';
        if (B(i,j) != Asave(i,j)) cout << "error #3" << '\n';
      }
    }
  }

  // Check that sample code in header works
  {
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    if (!cpu_A.is(A)) A = cpu_A;
    for (INTEGER j=1; j<=Asave.ncol(); ++j) {
      for (INTEGER i=1; i<=Asave.nrow(); ++i) {
        if (cpu_A(i-1,j-1) != Asave(i,j)) cout << "error #4" << '\n';
        if (A(i,j) != Asave(i,j)) cout << "error #5" << '\n';
      }
    }
  }

  // Check that method is works
  {
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    vclmat cpu_B = cpu_A;
    realmat B = cpu_B;
    cout << "Correct answers: true, false, false, true, false, false" << '\n';
    cout << "A progenitor of cpu_A " << cpu_A.is(A) << '\n';
    cout << "B progenitor of cpu_A " << cpu_A.is(B) << '\n';
    cout << "B progenitor of cpu_A " << cpu_B.is(A) << '\n';
    vclmat cpu_C(A);
    cout << "A progenitor of cpu_C " << cpu_C.is(A) << '\n';
    cpu_C = cpu_A;
    cout << "A progenitor of cpu_C " << cpu_C.is(A) << '\n';
    cpu_C = cpu_B;
    cout << "A progenitor of cpu_C " << cpu_C.is(A) << '\n';
  }

  // Check null constructor and conversion operator
  {
    cout << '\n';
    cout << "Matrix should be null";
    vclmat cpu_A;
    cout << realmat(cpu_A);  
  }

  // Check constructor and conversion operator
  {
    cout << '\n';
    cout << "Elements should be sequence from 1 to " << r*c;
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = i;
    vclmat cpu_A(A);
    cout << realmat(cpu_A);  
  } 

  // Check copy constructor and first operator()
  {
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    vclmat cpu_B = cpu_A;
    for (INTEGER j=1; j<=Asave.ncol(); ++j) {
      for (INTEGER i=1; i<=Asave.nrow(); ++i) {
        if (cpu_A(i-1,j-1) != Asave(i,j)) cout << "error #6" << '\n';
        if (cpu_B(i-1,j-1) != Asave(i,j)) cout << "error #7" << '\n';
      }
    }
    cout << '\n';
    cout << "Matrix should be zero";
    cout << realmat(cpu_A) - realmat(cpu_B);  
  } 

  // Check assignment operator and first operator()
  {
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    vclmat cpu_B;
    cpu_B = cpu_A;
    for (INTEGER j=1; j<=Asave.ncol(); ++j) {
      for (INTEGER i=1; i<=Asave.nrow(); ++i) {
        if (cpu_A(i-1,j-1) != Asave(i,j)) cout << "error #8" << '\n';
        if (cpu_B(i-1,j-1) != Asave(i,j)) cout << "error #9" << '\n';
      }
    }
    cout << '\n';
    cout << "Matrix should be zero";
    cout << realmat(cpu_A) - realmat(cpu_B);  
  } 

  // Check size methods
  {
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    cout << '\n';
    cout << "Should be " << r*c << ' ' << r << ' ' << c << '\n';
    cout << cpu_A.size() <<' '<< cpu_A.size1() <<' '<< cpu_A.size2() << '\n';
    cout << Asave.size() <<' '<< Asave.nrow() <<' '<< Asave.ncol() << '\n';
    if (INTEGER(cpu_A.size()) != A.size()) cout << "error #10" << '\n';
    if (INTEGER(cpu_A.size1()) != A.nrow()) cout << "error #11" << '\n';
    if (INTEGER(cpu_A.size2()) != A.ncol()) cout << "error #12" << '\n';
    cout << '\n';
  } 

  // Check second operator()
  {
    cout<<"First and third matrices below should be the same, the second zero";
    realmat A(r,c);
    for (INTEGER i=1; i<=A.size(); ++i) A[i] = fill_value();
    realmat Asave = A;
    vclmat cpu_A(A);
    realmat B(r,c,0.0);
    vclmat cpu_B(B);
    cout << realmat(cpu_A);
    cout << realmat(cpu_B);
    for (unsigned int j=0; j<cpu_A.size2(); ++j) {
      for (unsigned int i=0; i<cpu_A.size1(); ++i) {
        cpu_B(i,j) = cpu_A(i,j);
      }
    }
    cout << realmat(cpu_B);
    for (INTEGER j=1; j<=Asave.ncol(); ++j) {
      for (INTEGER i=1; i<=Asave.nrow(); ++i) {
        if (cpu_B(i-1,j-1) != Asave(i,j)) cout << "error #13" << '\n';
      }
    }
  } 


  return 0;
}
