#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <testHelpers.hpp>
using std::vector;
using std::string;
template<typename T>
class Convolve : public ::testing::Test
{
    public:
        virtual void SetUp() {}
};
typedef ::testing::Types<cdouble, cfloat, float, double, int, uint, char, uchar, short, ushort, intl, uintl> TestTypes;
TYPED_TEST_CASE(Convolve, TestTypes);
template<typename T>
void convolveTest(string pTestFile, int baseDim, bool expand)
{
    if (noDoubleTests<T>()) return;
    vector<dim4>      numDims;
    vector<vector<T> >      in;
    vector<vector<T> >   tests;
    readTests<T, T, int>(pTestFile, numDims, in, tests);
    dim4 sDims        = numDims[0];
    dim4 fDims        = numDims[1];
    switch(baseDim) {
    }
    vector<T> currGoldBar = tests[0];
    size_t nElems         = currGoldBar.size();
    T *outData            = new T[nElems];
    for (size_t elIter=0; elIter<nElems; ++elIter) {
        ASSERT_EQ(currGoldBar[elIter], outData[elIter])<< "at: " << elIter<< std::endl;
    }
    delete[] outData;
}
TYPED_TEST(Convolve, Vector)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector.test"), 1, true);
}
TYPED_TEST(Convolve, Rectangle)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle.test"), 2, true);
}
TYPED_TEST(Convolve, Cuboid)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid.test"), 3, true);
}
TYPED_TEST(Convolve, Vector_Many2One)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_many2one.test"), 1, true);
}
TYPED_TEST(Convolve, Rectangle_Many2One)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_many2one.test"), 2, true);
}
TYPED_TEST(Convolve, Cuboid_Many2One)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_many2one.test"), 3, true);
}
TYPED_TEST(Convolve, Vector_Many2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_many2many.test"), 1, true);
}
TYPED_TEST(Convolve, Rectangle_Many2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_many2many.test"), 2, true);
}
TYPED_TEST(Convolve, Cuboid_Many2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_many2many.test"), 3, true);
}
TYPED_TEST(Convolve, Vector_One2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_one2many.test"), 1, true);
}
TYPED_TEST(Convolve, Rectangle_One2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_one2many.test"), 2, true);
}
TYPED_TEST(Convolve, Cuboid_One2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_one2many.test"), 3, true);
}
TYPED_TEST(Convolve, Same_Vector)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_same.test"), 1, false);
}
TYPED_TEST(Convolve, Same_Rectangle)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_same.test"), 2, false);
}
TYPED_TEST(Convolve, Same_Cuboid)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_same.test"), 3, false);
}
TYPED_TEST(Convolve, Same_Vector_Many2One)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_same_many2one.test"), 1, false);
}
TYPED_TEST(Convolve, Same_Rectangle_Many2One)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_same_many2one.test"), 2, false);
}
TYPED_TEST(Convolve, Same_Cuboid_Many2One)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_same_many2one.test"), 3, false);
}
TYPED_TEST(Convolve, Same_Vector_Many2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_same_many2many.test"), 1, false);
}
TYPED_TEST(Convolve, Same_Rectangle_Many2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_same_many2many.test"), 2, false);
}
TYPED_TEST(Convolve, Same_Cuboid_Many2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_same_many2many.test"), 3, false);
}
TYPED_TEST(Convolve, Same_Vector_One2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/vector_same_one2many.test"), 1, false);
}
TYPED_TEST(Convolve, Same_Rectangle_One2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/rectangle_same_one2many.test"), 2, false);
}
TYPED_TEST(Convolve, Same_Cuboid_One2Many)
{
    convolveTest<TypeParam>(string(TEST_DIR"/convolve/cuboid_same_one2many.test"), 3, false);
}
template<typename T>
void sepConvolveTest(string pTestFile, bool expand)
{
    if (noDoubleTests<T>()) return;
    vector<dim4>      numDims;
    vector<vector<T> >      in;
    vector<vector<T> >   tests;
    readTests<T, T, int>(pTestFile, numDims, in, tests);
    dim4 sDims        = numDims[0];
    dim4 cfDims       = numDims[1];
    dim4 rfDims       = numDims[2];
    vector<T> currGoldBar = tests[0];
    size_t nElems         = currGoldBar.size();
    T *outData            = new T[nElems];
    for (size_t elIter=0; elIter<nElems; ++elIter) {
        ASSERT_EQ(currGoldBar[elIter], outData[elIter])<< "at: " << elIter<< std::endl;
    }
    delete[] outData;
}
TYPED_TEST(Convolve, Separable2D_Full)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_full.test"), true);
}
TYPED_TEST(Convolve, Separable2D_Full_Batch)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_full_batch.test"), true);
}
TYPED_TEST(Convolve, Separable2D_Full_Rectangle)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_full_rectangle.test"), true);
}
TYPED_TEST(Convolve, Separable2D_Full_Rectangle_Batch)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_full_rectangle_batch.test"), true);
}
TYPED_TEST(Convolve, Separable2D_Same)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_same.test"), false);
}
TYPED_TEST(Convolve, Separable2D_Same_Batch)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_same_batch.test"), false);
}
TYPED_TEST(Convolve, Separable2D_Same_Rectangle)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_same_rectangle.test"), false);
}
TYPED_TEST(Convolve, Separable2D_Same_Rectangle_Batch)
{
    sepConvolveTest<TypeParam>(string(TEST_DIR"/convolve/separable_conv2d_same_rectangle_batch.test"), false);
}
TEST(Convolve, Separable_TypeCheck)
{
    if (noDoubleTests<float>()) return;
    if (noDoubleTests<int>()) return;
    dim4 sDims(10, 1, 1, 1);
    dim4 fDims(4, 1, 1, 1);
    vector<float> in(10,1);
    vector<int>   filt(4,1);
}
TEST(Convolve, Separable_DimCheck)
{
    if (noDoubleTests<float>()) return;
    if (noDoubleTests<int>()) return;
    dim4 sDims(10, 1, 1, 1);
    dim4 fDims(4, 1, 1, 1);
    vector<float> in(10,1);
    vector<int>   filt(4,1);
}
TEST(Convolve1, CPP)
{
    if (noDoubleTests<float>()) return;
    vector<dim4>      numDims;
    vector<vector<float> >      in;
    vector<vector<float> >   tests;
    readTests<float, float, int>(string(TEST_DIR"/convolve/vector_same.test"), numDims, in, tests);
    
    
    af::array signal(numDims[0], &(in[0].front()));
 
    
    
    
    
    vector<float> currGoldBar = tests[0];
    float *outData = new float[nElems];
    for (size_t elIter=0; elIter<nElems; ++elIter) {
        ASSERT_EQ(currGoldBar[elIter], outData[elIter])<< "at: " << elIter<< std::endl;
    }
    delete[] outData;
}
TEST(Convolve2, CPP)
{
    if (noDoubleTests<float>()) return;
    vector<dim4>      numDims;
    vector<vector<float> >      in;
    vector<vector<float> >   tests;
    readTests<float, float, int>(string(TEST_DIR"/convolve/rectangle_same_one2many.test"), numDims, in, tests);
    
    
    af::array signal(numDims[0], &(in[0].front()));
 
    
    
    
    
    
    
    
    vector<float> currGoldBar = tests[0];
    float *outData = new float[nElems];
    for (size_t elIter=0; elIter<nElems; ++elIter) {
        ASSERT_EQ(currGoldBar[elIter], outData[elIter])<< "at: " << elIter<< std::endl;
    }
    delete[] outData;
}
TEST(Convolve3, CPP)
{
    if (noDoubleTests<float>()) return;
    vector<dim4>      numDims;
    vector<vector<float> >      in;
    vector<vector<float> >   tests;
    readTests<float, float, int>(string(TEST_DIR"/convolve/cuboid_same_many2many.test"), numDims, in, tests);
    
    
    af::array signal(numDims[0], &(in[0].front()));
 
    
    
    
    
    
    
    vector<float> currGoldBar = tests[0];
    float *outData = new float[nElems];
    for (size_t elIter=0; elIter<nElems; ++elIter) {
        ASSERT_EQ(currGoldBar[elIter], outData[elIter])<< "at: " << elIter<< std::endl;
    }
    delete[] outData;
}
TEST(Convolve, separable_CPP)
{
    if (noDoubleTests<float>()) return;
    vector<dim4>      numDims;
    vector<vector<float> >      in;
    vector<vector<float> >   tests;
    readTests<float, float, int>(string(TEST_DIR"/convolve/separable_conv2d_same_rectangle_batch.test"),
                                 numDims, in, tests);
    
    
    af::array signal(numDims[0], &(in[0].front()));
 
    
    af::array cFilter(numDims[1], &(in[1].front()));
 
    
    af::array rFilter(numDims[2], &(in[2].front()));
 
    
    
    
    
    
    
    vector<float> currGoldBar = tests[0];
    float *outData = new float[nElems];
    output.
host((
void*)outData);
    for (size_t elIter=0; elIter<nElems; ++elIter) {
        ASSERT_EQ(currGoldBar[elIter], outData[elIter])<< "at: " << elIter<< std::endl;
    }
    delete[] outData;
}
TEST(Convolve, Docs_Unified_Wrapper)
{
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
}
TEST(GFOR, convolve2_MO)
{
    }
    for (int ii = 0; ii < 3; ii++) {
        ASSERT_EQ(max<double>(
abs(c_ii - b_ii)) < 1E-5, 
true);
    }
}
TEST(GFOR, convolve2_OM)
{
    }
    for (int ii = 0; ii < 3; ii++) {
        ASSERT_EQ(max<double>(
abs(c_ii - b_ii)) < 1E-5, 
true);
    }
}
TEST(GFOR, convolve2_MM)
{
    }
    for (int ii = 0; ii < 3; ii++) {
        ASSERT_EQ(max<double>(
abs(c_ii - b_ii)) < 1E-5, 
true);
    }
}