#include <gtest/gtest.h>
#include <vector>
#include <testHelpers.hpp>
using namespace std;
TEST(BasicTests, constant1000x1000)
{
    if (noDoubleTests<float>()) return;
    static const int ndims = 2;
    static const int dim_size = 1000;
    dim_t d[ndims] = {dim_size, dim_size};
 
    double valA = 3.9;
    vector<float> h_a(dim_size * dim_size, 100);
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        ASSERT_FLOAT_EQ(valA, h_a[i]);
    }
}
TEST(BasicTests, constant10x10)
{
    if (noDoubleTests<float>()) return;
    static const int ndims = 2;
    static const int dim_size = 10;
    dim_t d[2] = {dim_size, dim_size};
 
    double valA = 3.9;
    vector<float> h_a(dim_size * dim_size, 0);
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        ASSERT_FLOAT_EQ(valA, h_a[i]);
    }
}
TEST(BasicTests, constant100x100)
{
    if (noDoubleTests<float>()) return;
    static const int ndims = 2;
    static const int dim_size = 100;
    dim_t d[2] = {dim_size, dim_size};
 
    double valA = 4.9;
    vector<float> h_a(dim_size * dim_size, 0);
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        ASSERT_FLOAT_EQ(valA, h_a[i]);
    }
}
TEST(BasicTests, AdditionSameType)
{
    if (noDoubleTests<float>()) return;
    if (noDoubleTests<double>()) return;
    static const int ndims = 2;
    static const int dim_size = 100;
    dim_t d[ndims] = {dim_size, dim_size};
 
    double valA = 3.9;
    double valB = 5.7;
    double  valCf = valA + valB;
    vector<float>  h_cf32 (dim_size * dim_size);
    vector<double> h_cf64 (dim_size * dim_size);
    double err = 0;
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        float df = h_cf32[i] - (valCf);
        ASSERT_FLOAT_EQ(valCf,  h_cf32[i]);
        ASSERT_FLOAT_EQ(valCf,  h_cf64[i]);
        err = err + df * df;
    }
    ASSERT_NEAR(0.0f, err, 1e-8);
}
TEST(BasicTests, Additionf64f64)
{
    if (noDoubleTests<double>()) return;
    static const int ndims = 2;
    static const int dim_size = 100;
    dim_t d[ndims] = {dim_size, dim_size};
 
    double valA = 3.9;
    double valB = 5.7;
    double valC = valA + valB;
    vector<double> h_c(dim_size * dim_size, 0);
    double err = 0;
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        double df = h_c[i] - (valC);
        ASSERT_FLOAT_EQ(valA + valB, h_c[i]);
        err = err + df * df;
    }
    ASSERT_NEAR(0.0f, err, 1e-8);
}
TEST(BasicTests, Additionf32f64)
{
    if (noDoubleTests<float>()) return;
    if (noDoubleTests<double>()) return;
    static const int ndims = 2;
    static const int dim_size = 100;
    dim_t d[ndims] = {dim_size, dim_size};
 
    double valA = 3.9;
    double valB = 5.7;
    double valC = valA + valB;
    vector<double> h_c(dim_size * dim_size);
    double err = 0;
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        double df = h_c[i] - (valC);
        ASSERT_FLOAT_EQ(valA + valB, h_c[i]);
        err = err + df * df;
    }
    ASSERT_NEAR(0.0f, err, 1e-8);
}
TEST(BasicArrayTests, constant10x10)
{
    if (noDoubleTests<float>()) return;
    double valA = 3.14;
    vector<float> h_a(dim_size * dim_size, 0);
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        ASSERT_FLOAT_EQ(valA, h_a[i]);
    }
}
TEST(BasicTests, constant100x100_CPP)
{
    if (noDoubleTests<float>()) return;
    static const int dim_size = 100;
    dim_t d[2] = {dim_size, dim_size};
 
    double valA = 4.9;
    dim4 dims(d[0], d[1]);
    vector<float> h_a(dim_size * dim_size, 0);
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        ASSERT_FLOAT_EQ(valA, h_a[i]);
    }
}
TEST(BasicTests, AdditionSameType_CPP)
{
    if (noDoubleTests<float>()) return;
    if (noDoubleTests<double>()) return;
    static const int dim_size = 100;
    dim_t d[2] = {dim_size, dim_size};
 
    dim4 dims(d[0], d[1]);
    double valA = 3.9;
    double valB = 5.7;
    double  valCf = valA + valB;
    vector<float>  h_cf32 (dim_size * dim_size);
    vector<double> h_cf64 (dim_size * dim_size);
    c32.
host((
void**)&h_cf32[0]);
    c64.
host((
void**)&h_cf64[0]);
    double err = 0;
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        float df = h_cf32[i] - (valCf);
        ASSERT_FLOAT_EQ(valCf,  h_cf32[i]);
        ASSERT_FLOAT_EQ(valCf,  h_cf64[i]);
        err = err + df * df;
    }
    ASSERT_NEAR(0.0f, err, 1e-8);
}
TEST(BasicTests, Additionf32f64_CPP)
{
    if (noDoubleTests<float>()) return;
    if (noDoubleTests<double>()) return;
    static const int dim_size = 100;
    dim_t d[2] = {dim_size, dim_size};
 
    dim4 dims(d[0], d[1]);
    double valA = 3.9;
    double valB = 5.7;
    double valC = valA + valB;
    vector<double> h_c(dim_size * dim_size);
    double err = 0;
    size_t elements = dim_size * dim_size;
    for(size_t i = 0; i < elements; i++) {
        double df = h_c[i] - (valC);
        ASSERT_FLOAT_EQ(valA + valB, h_c[i]);
        err = err + df * df;
    }
    ASSERT_NEAR(0.0f, err, 1e-8);
}