LCOV - code coverage report
Current view: top level - elsa/core/tests - test_DataContainer.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 1215 1215 100.0 %
Date: 2022-08-25 03:05:39 Functions: 40 40 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file test_DataContainer.cpp
       3             :  *
       4             :  * @brief Tests for DataContainer class
       5             :  *
       6             :  * @author Matthias Wieczorek - initial code
       7             :  * @author David Frank - rewrite to use doctest and BDD
       8             :  * @author Tobias Lasser - rewrite and added code coverage
       9             :  */
      10             : 
      11             : #include "doctest/doctest.h"
      12             : #include "DataContainer.h"
      13             : #include "IdenticalBlocksDescriptor.h"
      14             : #include "testHelpers.h"
      15             : #include "VolumeDescriptor.h"
      16             : 
      17             : #include <type_traits>
      18             : 
      19             : using namespace elsa;
      20             : using namespace doctest;
      21             : 
      22             : // Provides object to be used with TEMPLATE_PRODUCT_TEST_CASE, necessary because enum cannot be
      23             : // passed directly
      24             : template <typename T>
      25             : struct TestHelperGPU {
      26             :     static const DataHandlerType handler_t = DataHandlerType::GPU;
      27             :     using data_t = T;
      28             : };
      29             : 
      30             : // Provides object to be used with TEMPLATE_PRODUCT_TEST_CASE, necessary because enum cannot be
      31             : // passed directly
      32             : template <typename T>
      33             : struct TestHelperCPU {
      34             :     static const DataHandlerType handler_t = DataHandlerType::CPU;
      35             :     using data_t = T;
      36             : };
      37             : 
      38             : using CPUTypeTuple =
      39             :     std::tuple<TestHelperCPU<float>, TestHelperCPU<double>, TestHelperCPU<complex<float>>,
      40             :                TestHelperCPU<complex<double>>, TestHelperCPU<index_t>>;
      41             : 
      42             : TYPE_TO_STRING(TestHelperCPU<float>);
      43             : TYPE_TO_STRING(TestHelperCPU<double>);
      44             : TYPE_TO_STRING(TestHelperCPU<index_t>);
      45             : TYPE_TO_STRING(TestHelperCPU<complex<float>>);
      46             : TYPE_TO_STRING(TestHelperCPU<complex<double>>);
      47             : 
      48             : TYPE_TO_STRING(DataContainer<float>);
      49             : TYPE_TO_STRING(DataContainer<double>);
      50             : TYPE_TO_STRING(DataContainer<index_t>);
      51             : TYPE_TO_STRING(DataContainer<complex<float>>);
      52             : TYPE_TO_STRING(DataContainer<complex<double>>);
      53             : 
      54             : TYPE_TO_STRING(complex<float>);
      55             : TYPE_TO_STRING(complex<double>);
      56             : 
      57             : #ifdef ELSA_CUDA_VECTOR
      58             : using GPUTypeTuple =
      59             :     std::tuple<TestHelperGPU<float>, TestHelperGPU<double>, TestHelperGPU<complex<float>>,
      60             :                TestHelperGPU<complex<double>>, TestHelperGPU<index_t>>;
      61             : 
      62             : TYPE_TO_STRING(TestHelperGPU<float>);
      63             : TYPE_TO_STRING(TestHelperGPU<double>);
      64             : TYPE_TO_STRING(TestHelperGPU<index_t>);
      65             : TYPE_TO_STRING(TestHelperGPU<complex<float>>);
      66             : TYPE_TO_STRING(TestHelperGPU<complex<double>>);
      67             : #endif
      68             : 
      69             : TEST_SUITE_BEGIN("core");
      70             : 
      71             : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing construction", TestType,
      72             :                           datacontainer_construction)
      73          50 : {
      74          50 :     using data_t = typename TestType::data_t;
      75             : 
      76          50 :     INFO("Testing type: " << TypeName_v<const data_t>);
      77             : 
      78          50 :     GIVEN("a DataDescriptor")
      79          50 :     {
      80          20 :         IndexVector_t numCoeff(3);
      81          20 :         numCoeff << 17, 47, 91;
      82          20 :         VolumeDescriptor desc(numCoeff);
      83             : 
      84          20 :         WHEN("constructing an empty DataContainer")
      85          20 :         {
      86          10 :             DataContainer<data_t> dc(desc, TestType::handler_t);
      87             : 
      88          10 :             THEN("it has the correct DataDescriptor") { REQUIRE_EQ(dc.getDataDescriptor(), desc); }
      89             : 
      90          10 :             THEN("it has a data vector of correct size")
      91          10 :             {
      92           5 :                 REQUIRE_EQ(dc.getSize(), desc.getNumberOfCoefficients());
      93           5 :             }
      94          10 :         }
      95             : 
      96          20 :         WHEN("constructing an initialized DataContainer")
      97          20 :         {
      98          10 :             auto data = generateRandomMatrix<data_t>(desc.getNumberOfCoefficients());
      99             : 
     100          10 :             DataContainer<data_t> dc(desc, data, TestType::handler_t);
     101             : 
     102          10 :             THEN("it has the correct DataDescriptor") { REQUIRE_EQ(dc.getDataDescriptor(), desc); }
     103             : 
     104          10 :             THEN("it has correctly initialized data")
     105          10 :             {
     106           5 :                 REQUIRE_EQ(dc.getSize(), desc.getNumberOfCoefficients());
     107             : 
     108      363550 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     109           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], data[i]));
     110           5 :             }
     111          10 :         }
     112          20 :     }
     113             : 
     114          50 :     GIVEN("another DataContainer")
     115          50 :     {
     116          30 :         IndexVector_t numCoeff(2);
     117          30 :         numCoeff << 32, 57;
     118          30 :         VolumeDescriptor desc(numCoeff);
     119             : 
     120          30 :         DataContainer<data_t> otherDc(desc, TestType::handler_t);
     121             : 
     122          30 :         auto randVec = generateRandomMatrix<data_t>(otherDc.getSize());
     123       54750 :         for (index_t i = 0; i < otherDc.getSize(); ++i)
     124       54720 :             otherDc[i] = randVec(i);
     125             : 
     126          30 :         WHEN("copy constructing")
     127          30 :         {
     128           5 :             DataContainer dc(otherDc);
     129             : 
     130           5 :             THEN("it copied correctly")
     131           5 :             {
     132           5 :                 REQUIRE_EQ(dc.getDataDescriptor(), otherDc.getDataDescriptor());
     133           5 :                 REQUIRE_NE(&dc.getDataDescriptor(), &otherDc.getDataDescriptor());
     134             : 
     135           5 :                 REQUIRE_EQ(dc, otherDc);
     136           5 :             }
     137           5 :         }
     138             : 
     139          30 :         WHEN("copy assigning")
     140          30 :         {
     141           5 :             DataContainer<data_t> dc(desc, TestType::handler_t);
     142           5 :             dc = otherDc;
     143             : 
     144           5 :             THEN("it copied correctly")
     145           5 :             {
     146           5 :                 REQUIRE_EQ(dc.getDataDescriptor(), otherDc.getDataDescriptor());
     147           5 :                 REQUIRE_NE(&dc.getDataDescriptor(), &otherDc.getDataDescriptor());
     148             : 
     149           5 :                 REQUIRE_EQ(dc, otherDc);
     150           5 :             }
     151           5 :         }
     152             : 
     153          30 :         WHEN("move constructing")
     154          30 :         {
     155          10 :             DataContainer oldOtherDc(otherDc);
     156             : 
     157          10 :             DataContainer dc(std::move(otherDc));
     158             : 
     159          10 :             THEN("it moved correctly")
     160          10 :             {
     161           5 :                 REQUIRE_EQ(dc.getDataDescriptor(), oldOtherDc.getDataDescriptor());
     162             : 
     163           5 :                 REQUIRE_EQ(dc, oldOtherDc);
     164           5 :             }
     165             : 
     166          10 :             THEN("the moved from object is still valid (but empty)") { otherDc = dc; }
     167          10 :         }
     168             : 
     169          30 :         WHEN("move assigning")
     170          30 :         {
     171          10 :             DataContainer oldOtherDc(otherDc);
     172             : 
     173          10 :             DataContainer<data_t> dc(desc, TestType::handler_t);
     174          10 :             dc = std::move(otherDc);
     175             : 
     176          10 :             THEN("it moved correctly")
     177          10 :             {
     178           5 :                 REQUIRE_EQ(dc.getDataDescriptor(), oldOtherDc.getDataDescriptor());
     179             : 
     180           5 :                 REQUIRE_EQ(dc, oldOtherDc);
     181           5 :             }
     182             : 
     183          10 :             THEN("the moved from object is still valid (but empty)") { otherDc = dc; }
     184          10 :         }
     185          30 :     }
     186          50 : }
     187             : 
     188             : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing the reduction operations", TestType,
     189             :                           datacontainer_reduction)
     190           5 : {
     191           5 :     using data_t = typename TestType::data_t;
     192             : 
     193           5 :     INFO("Testing type: " << TypeName_v<const data_t>);
     194             : 
     195           5 :     GIVEN("a DataContainer")
     196           5 :     {
     197           5 :         IndexVector_t numCoeff(3);
     198           5 :         numCoeff << 11, 73, 45;
     199           5 :         VolumeDescriptor desc(numCoeff);
     200             : 
     201           5 :         WHEN("putting in some random data")
     202           5 :         {
     203           5 :             auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     204             : 
     205           5 :             THEN("the reductions work a expected")
     206           5 :             {
     207           5 :                 REQUIRE_UNARY(checkApproxEq(dc.sum(), randVec.sum()));
     208           5 :                 REQUIRE_UNARY(checkApproxEq(
     209           5 :                     dc.l0PseudoNorm(),
     210           5 :                     (randVec.array().cwiseAbs()
     211           5 :                      >= std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon())
     212           5 :                         .count()));
     213           5 :                 REQUIRE_UNARY(checkApproxEq(dc.l1Norm(), randVec.array().abs().sum()));
     214           5 :                 REQUIRE_UNARY(checkApproxEq(dc.lInfNorm(), randVec.array().abs().maxCoeff()));
     215           5 :                 REQUIRE_UNARY(checkApproxEq(dc.squaredL2Norm(), randVec.squaredNorm()));
     216             : 
     217           5 :                 auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     218             : 
     219           5 :                 REQUIRE_UNARY(checkApproxEq(dc.dot(dc2), randVec.dot(randVec2)));
     220             : 
     221           5 :                 if constexpr (isComplex<data_t>) {
     222           3 :                     CHECK_THROWS(dc.minElement());
     223           3 :                     CHECK_THROWS(dc.maxElement());
     224           3 :                 } else {
     225           3 :                     REQUIRE_UNARY(checkApproxEq(dc.minElement(), randVec.array().minCoeff()));
     226           3 :                     REQUIRE_UNARY(checkApproxEq(dc.maxElement(), randVec.array().maxCoeff()));
     227           3 :                 }
     228           5 :             }
     229           5 :         }
     230           5 :     }
     231           5 : }
     232             : 
     233             : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing element-wise access", TestType,
     234             :                           datacontainer_elemwise)
     235          75 : {
     236          75 :     using data_t = typename TestType::data_t;
     237             : 
     238          75 :     INFO("Testing type: " << TypeName_v<const data_t>);
     239             : 
     240          75 :     GIVEN("a DataContainer")
     241          75 :     {
     242           5 :         IndexVector_t numCoeff(2);
     243           5 :         numCoeff << 47, 11;
     244           5 :         VolumeDescriptor desc(numCoeff);
     245           5 :         DataContainer<data_t> dc(desc, TestType::handler_t);
     246             : 
     247           5 :         WHEN("accessing the elements")
     248           5 :         {
     249           5 :             IndexVector_t coord(2);
     250           5 :             coord << 17, 4;
     251           5 :             index_t index = desc.getIndexFromCoordinate(coord);
     252             : 
     253           5 :             THEN("it works as expected when using indices/coordinates")
     254           5 :             {
     255             :                 // For integral typeps don't have a floating point value
     256           5 :                 if constexpr (std::is_integral_v<data_t>) {
     257           4 :                     dc[index] = data_t(2);
     258           4 :                     REQUIRE_UNARY(checkApproxEq(dc[index], 2));
     259           4 :                     REQUIRE_UNARY(checkApproxEq(dc(coord), 2));
     260           4 :                     REQUIRE_UNARY(checkApproxEq(dc(17, 4), 2));
     261             : 
     262           4 :                     dc(coord) = data_t(3);
     263           4 :                     REQUIRE_UNARY(checkApproxEq(dc[index], 3));
     264           4 :                     REQUIRE_UNARY(checkApproxEq(dc(coord), 3));
     265           4 :                     REQUIRE_UNARY(checkApproxEq(dc(17, 4), 3));
     266           4 :                 } else {
     267           4 :                     dc[index] = data_t(2.2f);
     268           4 :                     REQUIRE_UNARY(checkApproxEq(dc[index], 2.2f));
     269           4 :                     REQUIRE_UNARY(checkApproxEq(dc(coord), 2.2f));
     270           4 :                     REQUIRE_UNARY(checkApproxEq(dc(17, 4), 2.2f));
     271             : 
     272           4 :                     dc(coord) = data_t(3.3f);
     273           4 :                     REQUIRE_UNARY(checkApproxEq(dc[index], 3.3f));
     274           4 :                     REQUIRE_UNARY(checkApproxEq(dc(coord), 3.3f));
     275           4 :                     REQUIRE_UNARY(checkApproxEq(dc(17, 4), 3.3f));
     276           4 :                 }
     277           5 :             }
     278           5 :         }
     279           5 :     }
     280             : 
     281          75 :     GIVEN("a DataContainer")
     282          75 :     {
     283          70 :         IndexVector_t numCoeff(2);
     284          70 :         numCoeff << 47, 11;
     285          70 :         VolumeDescriptor desc(numCoeff);
     286             : 
     287          70 :         WHEN("putting in some random data")
     288          70 :         {
     289          30 :             auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     290             : 
     291          30 :             THEN("the element-wise unary operations work as expected")
     292          30 :             {
     293           5 :                 DataContainer dcAbs = cwiseAbs(dc);
     294        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     295           5 :                     REQUIRE_UNARY(checkApproxEq(dcAbs[i], randVec.array().abs()[i]));
     296             : 
     297           5 :                 DataContainer dcSquare = square(dc);
     298        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     299           5 :                     REQUIRE_UNARY(checkApproxEq(dcSquare[i], randVec.array().square()[i]));
     300           5 :                 DataContainer dcSqrt = sqrt(dcSquare);
     301        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     302           5 :                     REQUIRE_UNARY(checkApproxEq(dcSqrt[i], randVec.array().square().sqrt()[i]));
     303             : 
     304             :                 // do exponent check only for floating point types as for integer will likely
     305             :                 // lead to overflow due to random init over full value range
     306           5 :                 if constexpr (!std::is_integral_v<data_t>) {
     307           4 :                     DataContainer dcExp = exp(dc);
     308        2072 :                     for (index_t i = 0; i < dc.getSize(); ++i)
     309           4 :                         REQUIRE_UNARY(checkApproxEq(dcExp[i], randVec.array().exp()[i]));
     310           4 :                 }
     311             : 
     312           5 :                 DataContainer dcLog = log(dcSquare);
     313        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     314           5 :                     REQUIRE_UNARY(checkApproxEq(dcLog[i], randVec.array().square().log()[i]));
     315             : 
     316           5 :                 DataContainer dcReal = real(dc);
     317        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     318           5 :                     REQUIRE_UNARY(checkApproxEq(dcReal[i], randVec.array().real()[i]));
     319             : 
     320           5 :                 DataContainer dcImag = imag(dc);
     321             : 
     322           5 :                 if constexpr (isComplex<data_t>) {
     323        1036 :                     for (index_t i = 0; i < dc.getSize(); ++i)
     324           3 :                         REQUIRE_UNARY(checkApproxEq(dcImag[i], randVec.array().imag()[i]));
     325           3 :                 } else {
     326        1554 :                     for (index_t i = 0; i < dc.getSize(); ++i)
     327           3 :                         REQUIRE_UNARY(checkApproxEq(dcImag[i], 0));
     328           3 :                 }
     329           5 :             }
     330             : 
     331          30 :             auto scalar = static_cast<data_t>(923.41f);
     332             : 
     333          30 :             THEN("the binary in-place addition of a scalar work as expected")
     334          30 :             {
     335           5 :                 dc += scalar;
     336        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     337           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) + scalar));
     338           5 :             }
     339             : 
     340          30 :             THEN("the binary in-place subtraction of a scalar work as expected")
     341          30 :             {
     342           5 :                 dc -= scalar;
     343        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     344           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) - scalar));
     345           5 :             }
     346             : 
     347          30 :             THEN("the binary in-place multiplication with a scalar work as expected")
     348          30 :             {
     349           5 :                 dc *= scalar;
     350        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     351           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) * scalar));
     352           5 :             }
     353             : 
     354          30 :             THEN("the binary in-place division by a scalar work as expected")
     355          30 :             {
     356           5 :                 dc /= scalar;
     357        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     358           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) / scalar));
     359           5 :             }
     360             : 
     361          30 :             THEN("the element-wise assignment of a scalar works as expected")
     362          30 :             {
     363           5 :                 dc = scalar;
     364        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     365           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], scalar));
     366           5 :             }
     367          30 :         }
     368             : 
     369          70 :         WHEN("having two containers with random data")
     370          70 :         {
     371          20 :             auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     372          20 :             auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     373             : 
     374          20 :             THEN("the element-wise in-place addition works as expected")
     375          20 :             {
     376           5 :                 dc += dc2;
     377        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     378           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) + randVec2(i)));
     379           5 :             }
     380             : 
     381          20 :             THEN("the element-wise in-place subtraction works as expected")
     382          20 :             {
     383           5 :                 dc -= dc2;
     384        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     385           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) - randVec2(i)));
     386           5 :             }
     387             : 
     388          20 :             THEN("the element-wise in-place multiplication works as expected")
     389          20 :             {
     390           5 :                 dc *= dc2;
     391        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     392           5 :                     REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) * randVec2(i)));
     393           5 :             }
     394             : 
     395          20 :             THEN("the element-wise in-place division works as expected")
     396          20 :             {
     397           5 :                 dc /= dc2;
     398        2590 :                 for (index_t i = 0; i < dc.getSize(); ++i)
     399        2585 :                     if (dc2[i] != data_t(0))
     400        2585 :                         REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) / randVec2(i)));
     401           5 :             }
     402          20 :         }
     403             : 
     404          70 :         WHEN("having two containers with real and complex data each")
     405          70 :         {
     406          20 :             auto [dcReals1, realsVec1] = generateRandomContainer<real_t>(desc, TestType::handler_t);
     407          20 :             auto [dcComps1, compsVec1] =
     408          20 :                 generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
     409             : 
     410          20 :             THEN("the element-wise maximum operation works as expected for two real "
     411          20 :                  "DataContainers")
     412          20 :             {
     413           5 :                 auto [dcReals2, realsVec2] =
     414           5 :                     generateRandomContainer<real_t>(desc, TestType::handler_t);
     415             : 
     416           5 :                 DataContainer dcCWiseMax = cwiseMax(dcReals1, dcReals2);
     417        2590 :                 for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
     418           5 :                     REQUIRE_UNARY(
     419           5 :                         checkApproxEq(dcCWiseMax[i], realsVec1.array().max(realsVec2.array())[i]));
     420           5 :             }
     421             : 
     422          20 :             THEN("the element-wise maximum operation works as expected for a real and a complex "
     423          20 :                  "DataContainer")
     424          20 :             {
     425           5 :                 auto [dcComps2, compsVec2] =
     426           5 :                     generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
     427             : 
     428           5 :                 DataContainer dcCWiseMax = cwiseMax(dcReals1, dcComps2);
     429        2590 :                 for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
     430           5 :                     REQUIRE_UNARY(checkApproxEq(dcCWiseMax[i],
     431           5 :                                                 realsVec1.array().max(compsVec2.array().abs())[i]));
     432           5 :             }
     433             : 
     434          20 :             THEN("the element-wise maximum operation works as expected for a complex and a real "
     435          20 :                  "DataContainer")
     436          20 :             {
     437           5 :                 auto [dcComps2, compsVec2] =
     438           5 :                     generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
     439             : 
     440           5 :                 DataContainer dcCWiseMax = cwiseMax(dcComps2, dcReals1);
     441        2590 :                 for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
     442           5 :                     REQUIRE_UNARY(checkApproxEq(dcCWiseMax[i],
     443           5 :                                                 compsVec2.array().abs().max(realsVec1.array())[i]));
     444           5 :             }
     445             : 
     446          20 :             THEN("the element-wise maximum operation works as expected for two DataContainers")
     447          20 :             {
     448           5 :                 auto [dcComps2, compsVec2] =
     449           5 :                     generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
     450             : 
     451           5 :                 DataContainer dcCWiseMax = cwiseMax(dcComps1, dcComps2);
     452        2590 :                 for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
     453           5 :                     REQUIRE_UNARY(checkApproxEq(
     454           5 :                         dcCWiseMax[i], compsVec1.array().abs().max(compsVec2.array().abs())[i]));
     455           5 :             }
     456          20 :         }
     457          70 :     }
     458          75 : }
     459             : 
     460             : TEST_CASE_TEMPLATE_DEFINE(
     461             :     "DataContainer: Testing the arithmetic operations with DataContainers arguments", TestType,
     462             :     datacontainer_arithmetic)
     463          10 : {
     464          10 :     using data_t = typename TestType::data_t;
     465             : 
     466          10 :     INFO("Testing type: " << TypeName_v<const data_t>);
     467             : 
     468          10 :     GIVEN("some DataContainers")
     469          10 :     {
     470          10 :         IndexVector_t numCoeff(3);
     471          10 :         numCoeff << 52, 7, 29;
     472          10 :         VolumeDescriptor desc(numCoeff);
     473             : 
     474          10 :         auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     475          10 :         auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
     476             : 
     477          10 :         THEN("the binary element-wise operations work as expected")
     478          10 :         {
     479           5 :             DataContainer resultPlus = dc + dc2;
     480       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     481           5 :                 REQUIRE_UNARY(checkApproxEq(resultPlus[i], dc[i] + dc2[i]));
     482             : 
     483           5 :             DataContainer resultMinus = dc - dc2;
     484       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     485           5 :                 REQUIRE_UNARY(checkApproxEq(resultMinus[i], dc[i] - dc2[i]));
     486             : 
     487           5 :             DataContainer resultMult = dc * dc2;
     488       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     489           5 :                 REQUIRE_UNARY(checkApproxEq(resultMult[i], dc[i] * dc2[i]));
     490             : 
     491           5 :             DataContainer resultDiv = dc / dc2;
     492       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     493       52780 :                 if (dc2[i] != data_t(0))
     494       52780 :                     REQUIRE_UNARY(checkApproxEq(resultDiv[i], dc[i] / dc2[i]));
     495           5 :         }
     496             : 
     497          10 :         THEN("the operations with a scalar work as expected")
     498          10 :         {
     499           5 :             data_t scalar = static_cast<data_t>(4.92f);
     500             : 
     501           5 :             DataContainer resultScalarPlus = scalar + dc;
     502       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     503           5 :                 REQUIRE_UNARY(checkApproxEq(resultScalarPlus[i], scalar + dc[i]));
     504             : 
     505           5 :             DataContainer resultPlusScalar = dc + scalar;
     506       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     507           5 :                 REQUIRE_UNARY(checkApproxEq(resultPlusScalar[i], dc[i] + scalar));
     508             : 
     509           5 :             DataContainer resultScalarMinus = scalar - dc;
     510       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     511           5 :                 REQUIRE_UNARY(checkApproxEq(resultScalarMinus[i], scalar - dc[i]));
     512             : 
     513           5 :             DataContainer resultMinusScalar = dc - scalar;
     514       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     515           5 :                 REQUIRE_UNARY(checkApproxEq(resultMinusScalar[i], dc[i] - scalar));
     516             : 
     517           5 :             DataContainer resultScalarMult = scalar * dc;
     518       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     519           5 :                 REQUIRE_UNARY(checkApproxEq(resultScalarMult[i], scalar * dc[i]));
     520             : 
     521           5 :             DataContainer resultMultScalar = dc * scalar;
     522       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     523           5 :                 REQUIRE_UNARY(checkApproxEq(resultMultScalar[i], dc[i] * scalar));
     524             : 
     525           5 :             DataContainer resultScalarDiv = scalar / dc;
     526       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     527       52780 :                 if (dc[i] != data_t(0))
     528       52780 :                     REQUIRE_UNARY(checkApproxEq(resultScalarDiv[i], scalar / dc[i]));
     529             : 
     530           5 :             DataContainer resultDivScalar = dc / scalar;
     531       52785 :             for (index_t i = 0; i < dc.getSize(); ++i)
     532           5 :                 REQUIRE_UNARY(checkApproxEq(resultDivScalar[i], dc[i] / scalar));
     533           5 :         }
     534          10 :     }
     535          10 : }
     536             : 
     537             : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing creation of Maps through DataContainer", TestType,
     538             :                           datacontainer_maps)
     539          20 : {
     540          20 :     using data_t = typename TestType::data_t;
     541             : 
     542          20 :     INFO("Testing type: " << TypeName_v<const data_t>);
     543             : 
     544          20 :     GIVEN("a non-blocked container")
     545          20 :     {
     546          10 :         IndexVector_t numCoeff(3);
     547          10 :         numCoeff << 52, 7, 29;
     548          10 :         VolumeDescriptor desc(numCoeff);
     549             : 
     550          10 :         DataContainer<data_t> dc(desc, TestType::handler_t);
     551          10 :         const DataContainer<data_t> constDc(desc, TestType::handler_t);
     552             : 
     553          10 :         WHEN("trying to reference a block")
     554          10 :         {
     555           5 :             THEN("an exception occurs")
     556           5 :             {
     557           5 :                 REQUIRE_THROWS(dc.getBlock(0));
     558           5 :                 REQUIRE_THROWS(constDc.getBlock(0));
     559           5 :             }
     560           5 :         }
     561             : 
     562          10 :         WHEN("creating a view")
     563          10 :         {
     564           5 :             IndexVector_t numCoeff(1);
     565           5 :             numCoeff << desc.getNumberOfCoefficients();
     566           5 :             VolumeDescriptor linearDesc(numCoeff);
     567           5 :             auto linearDc = dc.viewAs(linearDesc);
     568           5 :             auto linearConstDc = constDc.viewAs(linearDesc);
     569             : 
     570           5 :             THEN("view has the correct descriptor and data")
     571           5 :             {
     572           5 :                 REQUIRE_EQ(linearDesc, linearDc.getDataDescriptor());
     573           5 :                 REQUIRE_EQ(&linearDc[0], &dc[0]);
     574             : 
     575           5 :                 REQUIRE_EQ(linearDesc, linearConstDc.getDataDescriptor());
     576           5 :                 REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
     577             : 
     578           5 :                 AND_THEN("view is not a shallow copy")
     579           5 :                 {
     580           5 :                     const auto dcCopy = dc;
     581           5 :                     const auto constDcCopy = constDc;
     582             : 
     583           5 :                     linearDc[0] = 1;
     584           5 :                     REQUIRE_EQ(&linearDc[0], &dc[0]);
     585           5 :                     REQUIRE_NE(&linearDc[0], &dcCopy[0]);
     586             : 
     587           5 :                     linearConstDc[0] = 1;
     588           5 :                     REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
     589           5 :                     REQUIRE_NE(&linearConstDc[0], &constDcCopy[0]);
     590           5 :                 }
     591           5 :             }
     592           5 :         }
     593          10 :     }
     594             : 
     595          20 :     GIVEN("a blocked container")
     596          20 :     {
     597          10 :         IndexVector_t numCoeff(2);
     598          10 :         numCoeff << 52, 29;
     599          10 :         VolumeDescriptor desc(numCoeff);
     600          10 :         index_t numBlocks = 7;
     601          10 :         IdenticalBlocksDescriptor blockDesc(numBlocks, desc);
     602             : 
     603          10 :         DataContainer<data_t> dc(blockDesc, TestType::handler_t);
     604          10 :         const DataContainer<data_t> constDc(blockDesc, TestType::handler_t);
     605             : 
     606          10 :         WHEN("referencing a block")
     607          10 :         {
     608           5 :             THEN("block has the correct descriptor and data")
     609           5 :             {
     610          40 :                 for (index_t i = 0; i < numBlocks; i++) {
     611          35 :                     auto dcBlock = dc.getBlock(i);
     612          35 :                     const auto constDcBlock = constDc.getBlock(i);
     613             : 
     614          35 :                     REQUIRE_EQ(dcBlock.getDataDescriptor(), blockDesc.getDescriptorOfBlock(i));
     615          35 :                     REQUIRE_EQ(&dcBlock[0], &dc[0] + blockDesc.getOffsetOfBlock(i));
     616             : 
     617          35 :                     REQUIRE_EQ(constDcBlock.getDataDescriptor(), blockDesc.getDescriptorOfBlock(i));
     618          35 :                     REQUIRE_EQ(&constDcBlock[0], &constDc[0] + blockDesc.getOffsetOfBlock(i));
     619          35 :                 }
     620           5 :             }
     621           5 :         }
     622             : 
     623          10 :         WHEN("creating a view")
     624          10 :         {
     625           5 :             IndexVector_t numCoeff(1);
     626           5 :             numCoeff << blockDesc.getNumberOfCoefficients();
     627           5 :             VolumeDescriptor linearDesc(numCoeff);
     628           5 :             auto linearDc = dc.viewAs(linearDesc);
     629           5 :             auto linearConstDc = constDc.viewAs(linearDesc);
     630             : 
     631           5 :             THEN("view has the correct descriptor and data")
     632           5 :             {
     633           5 :                 REQUIRE_EQ(linearDesc, linearDc.getDataDescriptor());
     634           5 :                 REQUIRE_EQ(&linearDc[0], &dc[0]);
     635             : 
     636           5 :                 REQUIRE_EQ(linearDesc, linearConstDc.getDataDescriptor());
     637           5 :                 REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
     638             : 
     639           5 :                 AND_THEN("view is not a shallow copy")
     640           5 :                 {
     641           5 :                     const auto dcCopy = dc;
     642           5 :                     const auto constDcCopy = constDc;
     643             : 
     644           5 :                     linearDc[0] = 1;
     645           5 :                     REQUIRE_EQ(&linearDc[0], &dc[0]);
     646           5 :                     REQUIRE_NE(&linearDc[0], &dcCopy[0]);
     647             : 
     648           5 :                     linearConstDc[0] = 1;
     649           5 :                     REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
     650           5 :                     REQUIRE_NE(&linearConstDc[0], &constDcCopy[0]);
     651           5 :                 }
     652           5 :             }
     653           5 :         }
     654          10 :     }
     655          20 : }
     656             : 
     657             : #ifdef ELSA_CUDA_VECTOR
     658             : TEST_CASE_TEMPLATE("DataContainer: Testing load data to GPU and vice versa", TestType, float,
     659             :                    double, complex<float>, complex<double>, index_t)
     660             : {
     661             :     GIVEN("A CPU DataContainer with random data")
     662             :     {
     663             :         IndexVector_t numCoeff(3);
     664             :         numCoeff << 52, 7, 29;
     665             :         VolumeDescriptor desc(numCoeff);
     666             : 
     667             :         DataContainer<TestType> dcCPU(desc, DataHandlerType::CPU);
     668             :         DataContainer<TestType> dcGPU(desc, DataHandlerType::GPU);
     669             : 
     670             :         auto randVec = generateRandomMatrix<TestType>(dcCPU.getSize());
     671             : 
     672             :         for (index_t i = 0; i < dcCPU.getSize(); ++i) {
     673             :             dcCPU[i] = randVec(i);
     674             :             dcGPU[i] = randVec(i);
     675             :         }
     676             : 
     677             :         WHEN("Trying to call loadToCPU on CPU container")
     678             :         {
     679             :             THEN("Throws") { REQUIRE_THROWS(dcCPU.loadToCPU()); }
     680             :         }
     681             : 
     682             :         WHEN("Trying to call loadToGPU on GPU container")
     683             :         {
     684             :             THEN("Throws") { REQUIRE_THROWS(dcGPU.loadToGPU()); }
     685             :         }
     686             : 
     687             :         WHEN("Loading to GPU from CPU")
     688             :         {
     689             :             DataContainer dcGPU2 = dcCPU.loadToGPU();
     690             : 
     691             :             REQUIRE_EQ(dcGPU2.getDataHandlerType(), DataHandlerType::GPU);
     692             : 
     693             :             THEN("all elements have to be the same")
     694             :             {
     695             :                 for (index_t i = 0; i < dcCPU.getSize(); ++i) {
     696             :                     REQUIRE_UNARY(checkApproxEq(dcGPU2[i], dcGPU[i]));
     697             :                 }
     698             :             }
     699             :         }
     700             : 
     701             :         WHEN("Loading to CPU from GPU")
     702             :         {
     703             :             DataContainer dcCPU2 = dcGPU.loadToCPU();
     704             : 
     705             :             REQUIRE_EQ(dcCPU2.getDataHandlerType(), DataHandlerType::CPU);
     706             : 
     707             :             THEN("all elements have to be the same")
     708             :             {
     709             :                 for (index_t i = 0; i < dcCPU.getSize(); ++i) {
     710             :                     REQUIRE_UNARY(checkApproxEq(dcCPU2[i], dcGPU[i]));
     711             :                 }
     712             :             }
     713             :         }
     714             : 
     715             :         WHEN("copy-assigning a GPU to a CPU container")
     716             :         {
     717             :             dcCPU = dcGPU;
     718             : 
     719             :             THEN("it should be a GPU container")
     720             :             {
     721             :                 REQUIRE_EQ(dcCPU.getDataHandlerType(), DataHandlerType::GPU);
     722             :             }
     723             : 
     724             :             AND_THEN("they should be equal")
     725             :             {
     726             :                 REQUIRE_EQ(dcCPU, dcGPU);
     727             :                 REQUIRE_EQ(dcCPU.getSize(), dcGPU.getSize());
     728             : 
     729             :                 for (index_t i = 0; i < dcCPU.getSize(); ++i) {
     730             :                     REQUIRE_UNARY(checkApproxEq(dcCPU[i], dcGPU[i]));
     731             :                 }
     732             :             }
     733             :         }
     734             : 
     735             :         WHEN("copy-assigning a CPU to a GPU container")
     736             :         {
     737             :             dcGPU = dcCPU;
     738             : 
     739             :             THEN("it should be a GPU container")
     740             :             {
     741             :                 REQUIRE_EQ(dcGPU.getDataHandlerType(), DataHandlerType::CPU);
     742             :             }
     743             : 
     744             :             AND_THEN("they should be equal")
     745             :             {
     746             :                 REQUIRE_EQ(dcCPU, dcGPU);
     747             :                 REQUIRE_EQ(dcCPU.getSize(), dcGPU.getSize());
     748             : 
     749             :                 for (index_t i = 0; i < dcCPU.getSize(); ++i) {
     750             :                     REQUIRE_UNARY(checkApproxEq(dcCPU[i], dcGPU[i]));
     751             :                 }
     752             :             }
     753             :         }
     754             :     }
     755             : }
     756             : #endif
     757             : 
     758             : TEST_CASE("DataContainer: Testing iterators for DataContainer")
     759          12 : {
     760          12 :     GIVEN("A 1D container")
     761          12 :     {
     762           4 :         constexpr index_t size = 20;
     763           4 :         IndexVector_t numCoeff(1);
     764           4 :         numCoeff << size;
     765           4 :         VolumeDescriptor desc(numCoeff);
     766             : 
     767           4 :         DataContainer dc1(desc);
     768           4 :         DataContainer dc2(desc);
     769             : 
     770           4 :         Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size);
     771           4 :         Eigen::VectorXf randVec2 = Eigen::VectorXf::Random(size);
     772             : 
     773          84 :         for (index_t i = 0; i < size; ++i) {
     774          80 :             dc1[i] = randVec1(i);
     775          80 :             dc2[i] = randVec2(i);
     776          80 :         }
     777             : 
     778           4 :         THEN("We can iterate forward")
     779           4 :         {
     780           1 :             int i = 0;
     781          21 :             for (auto v = dc1.cbegin(); v != dc1.cend(); v++) {
     782          20 :                 REQUIRE_UNARY(checkApproxEq(*v, randVec1[i++]));
     783          20 :             }
     784           1 :             REQUIRE_EQ(i, size);
     785           1 :         }
     786             : 
     787           4 :         THEN("We can iterate backward")
     788           4 :         {
     789           1 :             int i = size;
     790          21 :             for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
     791          20 :                 REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
     792          20 :             }
     793           1 :             REQUIRE_EQ(i, 0);
     794           1 :         }
     795             : 
     796           4 :         THEN("We can iterate and mutate")
     797           4 :         {
     798           1 :             int i = 0;
     799          20 :             for (auto& v : dc1) {
     800          20 :                 v = v * 2;
     801          20 :                 REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
     802          20 :             }
     803           1 :             REQUIRE_EQ(i, size);
     804             : 
     805           1 :             i = 0;
     806          20 :             for (auto v : dc1) {
     807          20 :                 REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
     808          20 :             }
     809           1 :             REQUIRE_EQ(i, size);
     810           1 :         }
     811             : 
     812           4 :         THEN("We can use STL algorithms")
     813           4 :         {
     814           1 :             REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
     815           1 :             REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
     816           1 :         }
     817           4 :     }
     818          12 :     GIVEN("A 2D container")
     819          12 :     {
     820           4 :         constexpr index_t size = 20;
     821           4 :         IndexVector_t numCoeff(2);
     822           4 :         numCoeff << size, size;
     823           4 :         VolumeDescriptor desc(numCoeff);
     824             : 
     825           4 :         DataContainer dc1(desc);
     826             : 
     827           4 :         Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size);
     828             : 
     829        1604 :         for (index_t i = 0; i < dc1.getSize(); ++i) {
     830        1600 :             dc1[i] = randVec1[i];
     831        1600 :         }
     832             : 
     833           4 :         THEN("We can iterate forward")
     834           4 :         {
     835           1 :             int i = 0;
     836         400 :             for (auto v : dc1) {
     837         400 :                 REQUIRE_UNARY(checkApproxEq(v, randVec1[i++]));
     838         400 :             }
     839           1 :             REQUIRE_EQ(i, size * size);
     840           1 :         }
     841             : 
     842           4 :         THEN("We can iterate backward")
     843           4 :         {
     844           1 :             int i = size * size;
     845         401 :             for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
     846         400 :                 REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
     847         400 :             }
     848           1 :             REQUIRE_EQ(i, 0);
     849           1 :         }
     850             : 
     851           4 :         THEN("We can iterate and mutate")
     852           4 :         {
     853           1 :             int i = 0;
     854         400 :             for (auto& v : dc1) {
     855         400 :                 v = v * 2;
     856         400 :                 REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
     857         400 :             }
     858           1 :             REQUIRE_EQ(i, size * size);
     859             : 
     860           1 :             i = 0;
     861         400 :             for (auto v : dc1) {
     862         400 :                 REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
     863         400 :             }
     864           1 :             REQUIRE_EQ(i, size * size);
     865           1 :         }
     866             : 
     867           4 :         THEN("We can use STL algorithms")
     868           4 :         {
     869           1 :             REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
     870           1 :             REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
     871           1 :         }
     872           4 :     }
     873          12 :     GIVEN("A 3D container")
     874          12 :     {
     875           4 :         constexpr index_t size = 20;
     876           4 :         IndexVector_t numCoeff(3);
     877           4 :         numCoeff << size, size, size;
     878           4 :         VolumeDescriptor desc(numCoeff);
     879             : 
     880           4 :         DataContainer dc1(desc);
     881             : 
     882           4 :         Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size * size);
     883             : 
     884       32004 :         for (index_t i = 0; i < dc1.getSize(); ++i) {
     885       32000 :             dc1[i] = randVec1[i];
     886       32000 :         }
     887             : 
     888           4 :         THEN("We can iterate forward")
     889           4 :         {
     890           1 :             int i = 0;
     891        8000 :             for (auto v : dc1) {
     892        8000 :                 REQUIRE_UNARY(checkApproxEq(v, randVec1[i++]));
     893        8000 :             }
     894           1 :             REQUIRE_EQ(i, size * size * size);
     895           1 :         }
     896             : 
     897           4 :         THEN("We can iterate backward")
     898           4 :         {
     899           1 :             int i = size * size * size;
     900        8001 :             for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
     901        8000 :                 REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
     902        8000 :             }
     903           1 :             REQUIRE_EQ(i, 0);
     904           1 :         }
     905             : 
     906           4 :         THEN("We can iterate and mutate")
     907           4 :         {
     908           1 :             int i = 0;
     909        8000 :             for (auto& v : dc1) {
     910        8000 :                 v = v * 2;
     911        8000 :                 REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
     912        8000 :             }
     913           1 :             REQUIRE_EQ(i, size * size * size);
     914             : 
     915           1 :             i = 0;
     916        8000 :             for (auto v : dc1) {
     917        8000 :                 REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
     918        8000 :             }
     919           1 :             REQUIRE_EQ(i, size * size * size);
     920           1 :         }
     921             : 
     922           4 :         THEN("We can use STL algorithms")
     923           4 :         {
     924           1 :             REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
     925           1 :             REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
     926           1 :         }
     927           4 :     }
     928          12 : }
     929             : 
     930             : TEST_CASE_TEMPLATE("DataContainer: Clip a DataContainer", data_t, float, double)
     931          20 : {
     932          20 :     GIVEN("some 1D vectors")
     933          20 :     {
     934          16 :         index_t size = 7;
     935          16 :         IndexVector_t numCoeff(1);
     936          16 :         numCoeff << size;
     937          16 :         VolumeDescriptor desc(numCoeff);
     938             : 
     939          16 :         data_t min = 6;
     940          16 :         data_t max = 19;
     941             : 
     942          16 :         Vector_t<data_t> dataVec1(desc.getNumberOfCoefficients());
     943          16 :         dataVec1 << 6, 10, 7, 18, 10, 11, 9;
     944          16 :         Vector_t<data_t> expectedDataVec1(desc.getNumberOfCoefficients());
     945          16 :         expectedDataVec1 << 6, 10, 7, 18, 10, 11, 9;
     946             : 
     947          16 :         Vector_t<data_t> dataVec2(desc.getNumberOfCoefficients());
     948          16 :         dataVec2 << 4, -23, 7, 18, 18, 10, 10;
     949          16 :         Vector_t<data_t> expectedDataVec2(desc.getNumberOfCoefficients());
     950          16 :         expectedDataVec2 << min, min, 7, 18, 18, 10, 10;
     951             : 
     952          16 :         Vector_t<data_t> dataVec3(desc.getNumberOfCoefficients());
     953          16 :         dataVec3 << 14, 23, 7, 18, 20, 10, 10;
     954          16 :         Vector_t<data_t> expectedDataVec3(desc.getNumberOfCoefficients());
     955          16 :         expectedDataVec3 << 14, max, 7, 18, max, 10, 10;
     956             : 
     957          16 :         Vector_t<data_t> dataVec4(desc.getNumberOfCoefficients());
     958          16 :         dataVec4 << 1, 23, 5, 28, 20, 30, 0;
     959          16 :         Vector_t<data_t> expectedDataVec4(desc.getNumberOfCoefficients());
     960          16 :         expectedDataVec4 << min, max, min, max, max, max, min;
     961             : 
     962          16 :         WHEN("creating a data container out of a vector within bounds")
     963          16 :         {
     964           4 :             DataContainer dc(desc, dataVec1);
     965           4 :             auto clipped = clip(dc, min, max);
     966             : 
     967           4 :             THEN("the size of the clipped DataContainer is equal to that of the original container")
     968           4 :             {
     969           2 :                 REQUIRE_EQ(clipped.getSize(), size);
     970           2 :             }
     971             : 
     972           4 :             THEN("the values correspond to the original DataContainers")
     973           4 :             {
     974          16 :                 for (int i = 0; i < size; ++i) {
     975          14 :                     INFO("Error at position: ", i);
     976          14 :                     REQUIRE_EQ(clipped[i], expectedDataVec1[i]);
     977          14 :                 }
     978           2 :             }
     979           4 :         }
     980             : 
     981          16 :         WHEN("creating a data container out of a vector within or lower than the bounds")
     982          16 :         {
     983           4 :             DataContainer dc(desc, dataVec2);
     984           4 :             auto clipped = clip(dc, min, max);
     985             : 
     986           4 :             THEN("the size of the clipped DataContainer is equal to that of the original container")
     987           4 :             {
     988           2 :                 REQUIRE_EQ(clipped.getSize(), size);
     989           2 :             }
     990             : 
     991           4 :             THEN("the values correspond to the original DataContainers")
     992           4 :             {
     993          16 :                 for (int i = 0; i < size; ++i) {
     994          14 :                     INFO("Error at position: ", i);
     995          14 :                     REQUIRE_EQ(clipped[i], expectedDataVec2[i]);
     996          14 :                 }
     997           2 :             }
     998           4 :         }
     999             : 
    1000          16 :         WHEN("creating a data container out of a vector within or higher than the bounds")
    1001          16 :         {
    1002           4 :             DataContainer dc(desc, dataVec3);
    1003           4 :             auto clipped = clip(dc, min, max);
    1004             : 
    1005           4 :             THEN("the size of the clipped DataContainer is equal to that of the original container")
    1006           4 :             {
    1007           2 :                 REQUIRE_EQ(clipped.getSize(), size);
    1008           2 :             }
    1009             : 
    1010           4 :             THEN("the values correspond to the original DataContainers")
    1011           4 :             {
    1012          16 :                 for (int i = 0; i < size; ++i) {
    1013          14 :                     INFO("Error at position: ", i);
    1014          14 :                     REQUIRE_EQ(clipped[i], expectedDataVec3[i]);
    1015          14 :                 }
    1016           2 :             }
    1017           4 :         }
    1018             : 
    1019          16 :         WHEN("creating a data container out of a vector outside the bounds")
    1020          16 :         {
    1021           4 :             DataContainer dc(desc, dataVec4);
    1022           4 :             auto clipped = clip(dc, min, max);
    1023             : 
    1024           4 :             THEN("the size of the clipped DataContainer is equal to that of the original container")
    1025           4 :             {
    1026           2 :                 REQUIRE_EQ(clipped.getSize(), size);
    1027           2 :             }
    1028             : 
    1029           4 :             THEN("the values correspond to the original DataContainers")
    1030           4 :             {
    1031          16 :                 for (int i = 0; i < size; ++i) {
    1032          14 :                     INFO("Error at position: ", i);
    1033          14 :                     REQUIRE_EQ(clipped[i], expectedDataVec4[i]);
    1034          14 :                 }
    1035           2 :             }
    1036           4 :         }
    1037          16 :     }
    1038             : 
    1039          20 :     GIVEN("a 2D data container")
    1040          20 :     {
    1041           4 :         IndexVector_t numCoeff(2);
    1042           4 :         numCoeff << 3, 2;
    1043           4 :         VolumeDescriptor desc(numCoeff);
    1044             : 
    1045           4 :         data_t min = 0;
    1046           4 :         data_t max = 8;
    1047             : 
    1048           4 :         Vector_t<data_t> dataVec(desc.getNumberOfCoefficients());
    1049           4 :         dataVec << -19, -23, 7, 8, 20, 1;
    1050           4 :         Vector_t<data_t> expectedDataVec(desc.getNumberOfCoefficients());
    1051           4 :         expectedDataVec << min, min, 7, 8, max, 1;
    1052             : 
    1053           4 :         WHEN("creating a data container out of a vector within and outside of both bounds")
    1054           4 :         {
    1055           4 :             DataContainer dc(desc, dataVec);
    1056           4 :             auto clipped = clip(dc, min, max);
    1057             : 
    1058           4 :             THEN("the size of the clipped DataContainer is equal to that of the original container")
    1059           4 :             {
    1060           2 :                 REQUIRE_EQ(clipped.getSize(), desc.getNumberOfCoefficients());
    1061           2 :             }
    1062             : 
    1063           4 :             THEN("the values correspond to the original DataContainers")
    1064           4 :             {
    1065          14 :                 for (int i = 0; i < desc.getNumberOfCoefficients(); ++i) {
    1066          12 :                     INFO("Error at position: ", i);
    1067          12 :                     REQUIRE_EQ(clipped[i], expectedDataVec[i]);
    1068          12 :                 }
    1069           2 :             }
    1070           4 :         }
    1071           4 :     }
    1072          20 : }
    1073             : 
    1074             : TEST_CASE_TEMPLATE("DataContainer: Concatenate two DataContainers", data_t, float, double,
    1075             :                    complex<float>, complex<double>)
    1076          28 : {
    1077          28 :     GIVEN("Two equally sized 1D data containers")
    1078          28 :     {
    1079           8 :         constexpr index_t size = 20;
    1080           8 :         IndexVector_t numCoeff(1);
    1081           8 :         numCoeff << size;
    1082           8 :         VolumeDescriptor desc(numCoeff);
    1083             : 
    1084           8 :         Vector_t<data_t> randVec1 = Vector_t<data_t>::Random(size);
    1085           8 :         Vector_t<data_t> randVec2 = Vector_t<data_t>::Random(size);
    1086             : 
    1087           8 :         DataContainer dc1(desc, randVec1);
    1088           8 :         DataContainer dc2(desc, randVec2);
    1089             : 
    1090           8 :         auto concated = concatenate(dc1, dc2);
    1091           8 :         THEN("The size of the concatenated DataContainer is twice the original one")
    1092           8 :         {
    1093           4 :             REQUIRE_EQ(concated.getSize(), 2 * size);
    1094           4 :         }
    1095             : 
    1096           8 :         THEN("The values correspond to the original DataContainers")
    1097           8 :         {
    1098          84 :             for (int i = 0; i < size; ++i) {
    1099          80 :                 INFO("Error at position: ", i);
    1100          80 :                 REQUIRE_EQ(concated[i], randVec1[i]);
    1101          80 :             }
    1102             : 
    1103          84 :             for (int i = 0; i < size; ++i) {
    1104          80 :                 INFO("Error at position: ", i + size);
    1105          80 :                 REQUIRE_EQ(concated[i + size], randVec2[i]);
    1106          80 :             }
    1107           4 :         }
    1108           8 :     }
    1109             : 
    1110          28 :     GIVEN("Two differently sized 1D data containers")
    1111          28 :     {
    1112           8 :         IndexVector_t numCoeff(1);
    1113             : 
    1114           8 :         constexpr index_t size1 = 20;
    1115           8 :         numCoeff[0] = size1;
    1116           8 :         VolumeDescriptor desc1(numCoeff);
    1117             : 
    1118           8 :         constexpr index_t size2 = 10;
    1119           8 :         numCoeff[0] = size2;
    1120           8 :         VolumeDescriptor desc2(numCoeff);
    1121             : 
    1122           8 :         Vector_t<data_t> randVec1 = Vector_t<data_t>::Random(size1);
    1123           8 :         Vector_t<data_t> randVec2 = Vector_t<data_t>::Random(size2);
    1124             : 
    1125           8 :         DataContainer dc1(desc1, randVec1);
    1126           8 :         DataContainer dc2(desc2, randVec2);
    1127             : 
    1128           8 :         auto concated = concatenate(dc1, dc2);
    1129             : 
    1130           8 :         THEN("The size of the concatenated DataContainer is twice the original one")
    1131           8 :         {
    1132           4 :             REQUIRE_EQ(concated.getSize(), size1 + size2);
    1133           4 :         }
    1134             : 
    1135           8 :         THEN("The values correspond to the original DataContainers")
    1136           8 :         {
    1137          84 :             for (int i = 0; i < size1; ++i) {
    1138          80 :                 INFO("Error at position: ", i);
    1139          80 :                 REQUIRE_EQ(concated[i], randVec1[i]);
    1140          80 :             }
    1141             : 
    1142          44 :             for (int i = 0; i < size2; ++i) {
    1143          40 :                 INFO("Error at position: ", i + size1);
    1144          40 :                 REQUIRE_EQ(concated[i + size1], randVec2[i]);
    1145          40 :             }
    1146           4 :         }
    1147           8 :     }
    1148             : 
    1149          28 :     GIVEN("Two equally sized 2D data containers")
    1150          28 :     {
    1151           8 :         constexpr index_t size = 20;
    1152           8 :         IndexVector_t numCoeff(2);
    1153           8 :         numCoeff << size, size;
    1154           8 :         VolumeDescriptor desc(numCoeff);
    1155             : 
    1156           8 :         Vector_t<data_t> randVec1 = Vector_t<data_t>::Random(size * size);
    1157           8 :         Vector_t<data_t> randVec2 = Vector_t<data_t>::Random(size * size);
    1158             : 
    1159           8 :         DataContainer dc1(desc, randVec1);
    1160           8 :         DataContainer dc2(desc, randVec2);
    1161             : 
    1162           8 :         auto concated = concatenate(dc1, dc2);
    1163           8 :         THEN("The size of the concatenated DataContainer is twice the original one")
    1164           8 :         {
    1165           4 :             REQUIRE_EQ(concated.getSize(), 2 * (size * size));
    1166           4 :         }
    1167             : 
    1168           8 :         THEN("The values correspond to the original DataContainers")
    1169           8 :         {
    1170        1604 :             for (int i = 0; i < size * size; ++i) {
    1171        1600 :                 INFO("Error at position: ", i);
    1172        1600 :                 REQUIRE_EQ(concated[i], randVec1[i]);
    1173        1600 :             }
    1174             : 
    1175        1604 :             for (int i = 0; i < size * size; ++i) {
    1176        1600 :                 INFO("Error at position: ", i + size);
    1177        1600 :                 REQUIRE_EQ(concated[i + size * size], randVec2[i]);
    1178        1600 :             }
    1179           4 :         }
    1180           8 :     }
    1181             : 
    1182          28 :     GIVEN("DataContainers of different dimension")
    1183          28 :     {
    1184           4 :         IndexVector_t numCoeff1D(1);
    1185           4 :         numCoeff1D << 20;
    1186           4 :         VolumeDescriptor desc1D(numCoeff1D);
    1187             : 
    1188           4 :         IndexVector_t numCoeff2D(2);
    1189           4 :         numCoeff2D << 20, 20;
    1190           4 :         VolumeDescriptor desc2D(numCoeff2D);
    1191             : 
    1192           4 :         DataContainer dc1(desc1D);
    1193           4 :         DataContainer dc2(desc2D);
    1194             : 
    1195           4 :         THEN("The concatenation throws") { REQUIRE_THROWS_AS(concatenate(dc1, dc2), LogicError); }
    1196           4 :     }
    1197          28 : }
    1198             : 
    1199             : TEST_CASE_TEMPLATE("DataContainer: Slice a DataContainer", data_t, float, double, complex<float>,
    1200             :                    complex<double>)
    1201          72 : {
    1202             :     // Set seed for Eigen Matrices!
    1203          72 :     srand((unsigned int) 666);
    1204             : 
    1205          72 :     GIVEN("A non 3D DataContainer")
    1206          72 :     {
    1207          16 :         constexpr index_t size = 20;
    1208          16 :         IndexVector_t numCoeff2D(2);
    1209          16 :         numCoeff2D << size, size;
    1210             : 
    1211          16 :         const VolumeDescriptor desc(numCoeff2D);
    1212          16 :         const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
    1213          16 :         const DataContainer<data_t> dc(desc, randVec);
    1214             : 
    1215          16 :         THEN("Accessing an out of bounds slice throws")
    1216          16 :         {
    1217           4 :             REQUIRE_THROWS_AS(dc.slice(20), LogicError);
    1218           4 :         }
    1219             : 
    1220          16 :         WHEN("Accessing all the slices")
    1221          16 :         {
    1222         252 :             for (int i = 0; i < size; ++i) {
    1223         240 :                 auto slice = dc.slice(i);
    1224             : 
    1225         240 :                 THEN("The the slice is a 2D slice of \"thickness\" 1")
    1226         240 :                 {
    1227           4 :                     REQUIRE_EQ(slice.getDataDescriptor().getNumberOfDimensions(), 2);
    1228             : 
    1229           4 :                     auto coeffs = slice.getDataDescriptor().getNumberOfCoefficientsPerDimension();
    1230           4 :                     auto expectedCoeffs = IndexVector_t(2);
    1231           4 :                     expectedCoeffs << size, 1;
    1232           4 :                     REQUIRE_EQ(coeffs, expectedCoeffs);
    1233           4 :                 }
    1234             : 
    1235         240 :                 THEN("All values are the same as of the original DataContainer")
    1236         240 :                 {
    1237             :                     // Check that it's read correctly
    1238           4 :                     auto vecSlice = randVec.segment(i * size, size);
    1239          84 :                     for (int j = 0; j < size; ++j) {
    1240          80 :                         REQUIRE_UNARY(checkApproxEq(slice(j, 0), vecSlice[j]));
    1241          80 :                     }
    1242           4 :                 }
    1243         240 :             }
    1244          12 :         }
    1245          16 :     }
    1246             : 
    1247          72 :     GIVEN("A const 3D DataContainer")
    1248          72 :     {
    1249          16 :         constexpr index_t size = 20;
    1250             : 
    1251          16 :         const VolumeDescriptor desc({size, size, size});
    1252          16 :         const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * size);
    1253          16 :         const DataContainer<data_t> dc(desc, randVec);
    1254             : 
    1255          16 :         THEN("Accessing an out of bounds slice throws")
    1256          16 :         {
    1257           4 :             REQUIRE_THROWS_AS(dc.slice(20), LogicError);
    1258           4 :         }
    1259             : 
    1260          16 :         WHEN("Accessing all the slices")
    1261          16 :         {
    1262         252 :             for (int i = 0; i < size; ++i) {
    1263         240 :                 auto slice = dc.slice(i);
    1264             : 
    1265         240 :                 THEN("The the slice is a 3D slice of \"thickness\" 1")
    1266         240 :                 {
    1267           4 :                     REQUIRE_EQ(slice.getDataDescriptor().getNumberOfDimensions(), 3);
    1268             : 
    1269           4 :                     auto coeffs = slice.getDataDescriptor().getNumberOfCoefficientsPerDimension();
    1270           4 :                     auto expectedCoeffs = IndexVector_t(3);
    1271           4 :                     expectedCoeffs << size, size, 1;
    1272           4 :                     REQUIRE_EQ(coeffs, expectedCoeffs);
    1273           4 :                 }
    1274             : 
    1275         240 :                 THEN("All values are the same as of the original DataContainer")
    1276         240 :                 {
    1277             :                     // Check that it's read correctly
    1278           4 :                     auto vecSlice = randVec.segment(i * size * size, size * size);
    1279          84 :                     for (int j = 0; j < size; ++j) {
    1280        1680 :                         for (int k = 0; k < size; ++k) {
    1281        1600 :                             REQUIRE_UNARY(checkApproxEq(slice(k, j, 0), vecSlice[k + j * size]));
    1282        1600 :                         }
    1283          80 :                     }
    1284           4 :                 }
    1285         240 :             }
    1286          12 :         }
    1287          16 :     }
    1288             : 
    1289          72 :     GIVEN("A non-const 3D DataContainer")
    1290          72 :     {
    1291          32 :         constexpr index_t size = 20;
    1292          32 :         IndexVector_t numCoeff(3);
    1293          32 :         numCoeff << size, size, size;
    1294             : 
    1295          32 :         const VolumeDescriptor desc(numCoeff);
    1296          32 :         DataContainer<data_t> dc(desc);
    1297          32 :         dc = 0;
    1298             : 
    1299          32 :         THEN("Accessing an out of bounds slice throws")
    1300          32 :         {
    1301           4 :             REQUIRE_THROWS_AS(dc.slice(20), LogicError);
    1302           4 :         }
    1303             : 
    1304          32 :         WHEN("Setting the first slice to 1")
    1305          32 :         {
    1306           8 :             dc.slice(0) = 1;
    1307             : 
    1308           8 :             THEN("Only the first slice is set to 1")
    1309           8 :             {
    1310          84 :                 for (int j = 0; j < size; ++j) {
    1311        1680 :                     for (int i = 0; i < size; ++i) {
    1312        1600 :                         data_t val = dc(i, j, 0);
    1313        1600 :                         INFO("Expected slice 0 to be ", data_t{1}, " but it's ", val, " (at (", i,
    1314        1600 :                              ", ", j, ", 0))");
    1315        1600 :                         REQUIRE_UNARY(checkApproxEq(val, 1));
    1316        1600 :                     }
    1317          80 :                 }
    1318           4 :             }
    1319             : 
    1320           8 :             THEN("The other slices are still set to 0")
    1321           8 :             {
    1322          80 :                 for (int k = 1; k < size; ++k) {
    1323        1596 :                     for (int j = 0; j < size; ++j) {
    1324       31920 :                         for (int i = 0; i < size; ++i) {
    1325       30400 :                             data_t val = dc(i, j, k);
    1326       30400 :                             INFO("Expected all slices but the first to be ", data_t{0},
    1327       30400 :                                  " but it's ", val, " (at (", i, ", ", j, ", 0))");
    1328       30400 :                             REQUIRE_UNARY(checkApproxEq(val, 0));
    1329       30400 :                         }
    1330        1520 :                     }
    1331          76 :                 }
    1332           4 :             }
    1333           8 :         }
    1334             : 
    1335          32 :         WHEN("Setting the fifth slice to some random data using a 3D DataContainer")
    1336          32 :         {
    1337          12 :             Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * 1);
    1338          12 :             const DataContainer slice(VolumeDescriptor({size, size, 1}), randVec);
    1339             : 
    1340          12 :             dc.slice(5) = slice;
    1341          12 :             THEN("The first 4 slices are still zero")
    1342          12 :             {
    1343          24 :                 for (int k = 0; k < 5; ++k) {
    1344         420 :                     for (int j = 0; j < size; ++j) {
    1345        8400 :                         for (int i = 0; i < size; ++i) {
    1346        8000 :                             data_t val = dc(i, j, k);
    1347             : 
    1348        8000 :                             INFO("Expected all slices but the first to be ", data_t{0},
    1349        8000 :                                  " but it's ", val, " (at (", i, ", ", j, ", 0))");
    1350        8000 :                             REQUIRE_UNARY(checkApproxEq(val, 0));
    1351        8000 :                         }
    1352         400 :                     }
    1353          20 :                 }
    1354           4 :             }
    1355             : 
    1356          12 :             THEN("The fifth slices set correctly")
    1357          12 :             {
    1358          84 :                 for (int j = 0; j < size; ++j) {
    1359        1680 :                     for (int i = 0; i < size; ++i) {
    1360        1600 :                         data_t val = dc(i, j, 5);
    1361        1600 :                         auto expected = randVec[i + j * size];
    1362        1600 :                         INFO("Expected slice 0 to be ", expected, " but it's ", val, " (at (", i,
    1363        1600 :                              ", ", j, ", 0))");
    1364        1600 :                         REQUIRE_UNARY(checkApproxEq(val, expected));
    1365        1600 :                     }
    1366          80 :                 }
    1367           4 :             }
    1368             : 
    1369          12 :             THEN("The last 14 slices are still zero")
    1370          12 :             {
    1371             :                 // Check last slices
    1372          60 :                 for (int k = 6; k < size; ++k) {
    1373        1176 :                     for (int j = 0; j < size; ++j) {
    1374       23520 :                         for (int i = 0; i < size; ++i) {
    1375       22400 :                             data_t val = dc(i, j, k);
    1376             : 
    1377       22400 :                             INFO("Expected all slices but the first to be ", data_t{0},
    1378       22400 :                                  " but it's ", val, " (at (", i, ", ", j, ", 0))");
    1379       22400 :                             REQUIRE_UNARY(checkApproxEq(val, 0));
    1380       22400 :                         }
    1381        1120 :                     }
    1382          56 :                 }
    1383           4 :             }
    1384          12 :         }
    1385             : 
    1386          32 :         WHEN("Setting the first slice to some random data using a 2D DataContainer")
    1387          32 :         {
    1388           8 :             Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
    1389           8 :             const DataContainer slice(VolumeDescriptor({size, size}), randVec);
    1390             : 
    1391           8 :             dc.slice(0) = slice;
    1392           8 :             THEN("The fifth slices set correctly")
    1393           8 :             {
    1394          84 :                 for (int j = 0; j < size; ++j) {
    1395        1680 :                     for (int i = 0; i < size; ++i) {
    1396        1600 :                         data_t val = dc(i, j, 0);
    1397        1600 :                         auto expected = randVec[i + j * size];
    1398        1600 :                         INFO("Expected slice 0 to be ", expected, " but it's ", val, " (at (", i,
    1399        1600 :                              ", ", j, ", 0))");
    1400        1600 :                         REQUIRE_UNARY(checkApproxEq(val, expected));
    1401        1600 :                     }
    1402          80 :                 }
    1403           4 :             }
    1404           8 :             THEN("The other slices are still zero")
    1405           8 :             {
    1406          80 :                 for (int k = 1; k < size; ++k) {
    1407        1596 :                     for (int j = 0; j < size; ++j) {
    1408       31920 :                         for (int i = 0; i < size; ++i) {
    1409       30400 :                             data_t val = dc(i, j, k);
    1410       30400 :                             INFO("Expected all slices but the first to be ", data_t{0},
    1411       30400 :                                  " but it's ", val, " (at (", i, ", ", j, ", 0))");
    1412       30400 :                             REQUIRE_UNARY(checkApproxEq(val, 0));
    1413       30400 :                         }
    1414        1520 :                     }
    1415          76 :                 }
    1416           4 :             }
    1417           8 :         }
    1418          32 :     }
    1419             : 
    1420          72 :     GIVEN("a 3D DataDescriptor and a 3D random Vector")
    1421          72 :     {
    1422           8 :         constexpr index_t size = 28;
    1423           8 :         constexpr index_t one = 1;
    1424           8 :         IndexVector_t numCoeff3D(3);
    1425           8 :         numCoeff3D << size, size, one;
    1426             : 
    1427           8 :         const VolumeDescriptor desc(numCoeff3D);
    1428           8 :         const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * one);
    1429             : 
    1430           8 :         WHEN("slicing a non-const DataContainer with the size of the last dimension of 1")
    1431           8 :         {
    1432           4 :             DataContainer<data_t> dc(desc, randVec);
    1433             : 
    1434           4 :             DataContainer<data_t> res = dc.slice(0);
    1435             : 
    1436           4 :             THEN("the DataContainers match") { REQUIRE_EQ(dc, res); }
    1437           4 :         }
    1438             : 
    1439           8 :         WHEN("slicing a const DataContainer with the size of the last dimension of 1")
    1440           8 :         {
    1441           4 :             const DataContainer<data_t> dc(desc, randVec);
    1442             : 
    1443           4 :             const DataContainer<data_t> res = dc.slice(0);
    1444             : 
    1445           4 :             THEN("the DataContainers match") { REQUIRE_EQ(dc, res); }
    1446           4 :         }
    1447           8 :     }
    1448          72 : }
    1449             : 
    1450             : TEST_CASE_TEMPLATE("DataContainer: FFT shift and IFFT shift a DataContainer", data_t, float, double,
    1451             :                    complex<float>, complex<double>)
    1452          48 : {
    1453          48 :     GIVEN("a one-element 2D data container")
    1454          48 :     {
    1455          16 :         DataContainer<data_t> dc(VolumeDescriptor{{1, 1}});
    1456          16 :         dc[0] = 8;
    1457          16 :         WHEN("running the FFT shift operation to the container")
    1458          16 :         {
    1459           8 :             DataContainer<data_t> fftShiftedDC = fftShift2D(dc);
    1460           8 :             THEN("the data descriptors match")
    1461           8 :             {
    1462           4 :                 REQUIRE_EQ(dc.getDataDescriptor(), fftShiftedDC.getDataDescriptor());
    1463           4 :             }
    1464           8 :             THEN("the data containers match") { REQUIRE_UNARY(fftShiftedDC == dc); }
    1465           8 :         }
    1466             : 
    1467          16 :         WHEN("running the IFFT shift operation to the container")
    1468          16 :         {
    1469           8 :             DataContainer<data_t> ifftShiftedDC = ifftShift2D(dc);
    1470           8 :             THEN("the data descriptors match")
    1471           8 :             {
    1472           4 :                 REQUIRE_EQ(dc.getDataDescriptor(), ifftShiftedDC.getDataDescriptor());
    1473           4 :             }
    1474           8 :             THEN("the data containers match") { REQUIRE_UNARY(ifftShiftedDC == dc); }
    1475           8 :         }
    1476          16 :     }
    1477             : 
    1478          48 :     GIVEN("a 3x3 2D data container")
    1479          48 :     {
    1480          16 :         DataContainer<data_t> dc(VolumeDescriptor{{3, 3}});
    1481          16 :         dc(0, 0) = 0;
    1482          16 :         dc(0, 1) = 1;
    1483          16 :         dc(0, 2) = 2;
    1484          16 :         dc(1, 0) = 3;
    1485          16 :         dc(1, 1) = 4;
    1486          16 :         dc(1, 2) = -4;
    1487          16 :         dc(2, 0) = -3;
    1488          16 :         dc(2, 1) = -2;
    1489          16 :         dc(2, 2) = -1;
    1490             : 
    1491          16 :         DataContainer<data_t> expectedFFTShiftDC(VolumeDescriptor{{3, 3}});
    1492          16 :         expectedFFTShiftDC(0, 0) = -1;
    1493          16 :         expectedFFTShiftDC(0, 1) = -3;
    1494          16 :         expectedFFTShiftDC(0, 2) = -2;
    1495          16 :         expectedFFTShiftDC(1, 0) = 2;
    1496          16 :         expectedFFTShiftDC(1, 1) = 0;
    1497          16 :         expectedFFTShiftDC(1, 2) = 1;
    1498          16 :         expectedFFTShiftDC(2, 0) = -4;
    1499          16 :         expectedFFTShiftDC(2, 1) = 3;
    1500          16 :         expectedFFTShiftDC(2, 2) = 4;
    1501             : 
    1502          16 :         WHEN("running the FFT shift operation to the container")
    1503          16 :         {
    1504           8 :             DataContainer<data_t> fftShiftedDC = fftShift2D(dc);
    1505           8 :             THEN("the data descriptors match")
    1506           8 :             {
    1507           4 :                 REQUIRE_EQ(fftShiftedDC.getDataDescriptor(),
    1508           4 :                            expectedFFTShiftDC.getDataDescriptor());
    1509           4 :             }
    1510           8 :             THEN("the data containers match") { REQUIRE_UNARY(fftShiftedDC == expectedFFTShiftDC); }
    1511           8 :         }
    1512             : 
    1513          16 :         DataContainer<data_t> expectedIFFTShiftDC(VolumeDescriptor{{3, 3}});
    1514          16 :         expectedIFFTShiftDC(0, 0) = 4;
    1515          16 :         expectedIFFTShiftDC(0, 1) = -4;
    1516          16 :         expectedIFFTShiftDC(0, 2) = 3;
    1517          16 :         expectedIFFTShiftDC(1, 0) = -2;
    1518          16 :         expectedIFFTShiftDC(1, 1) = -1;
    1519          16 :         expectedIFFTShiftDC(1, 2) = -3;
    1520          16 :         expectedIFFTShiftDC(2, 0) = 1;
    1521          16 :         expectedIFFTShiftDC(2, 1) = 2;
    1522          16 :         expectedIFFTShiftDC(2, 2) = 0;
    1523             : 
    1524          16 :         WHEN("running the IFFT shift operation to the container")
    1525          16 :         {
    1526           8 :             DataContainer<data_t> ifftShiftedDC = ifftShift2D(dc);
    1527           8 :             THEN("the data descriptors match")
    1528           8 :             {
    1529           4 :                 REQUIRE_EQ(ifftShiftedDC.getDataDescriptor(),
    1530           4 :                            expectedIFFTShiftDC.getDataDescriptor());
    1531           4 :             }
    1532           8 :             THEN("the data containers match")
    1533           8 :             {
    1534           4 :                 REQUIRE_UNARY(ifftShiftedDC == expectedIFFTShiftDC);
    1535           4 :             }
    1536           8 :         }
    1537          16 :     }
    1538             : 
    1539          48 :     GIVEN("a 5x5 2D data container")
    1540          48 :     {
    1541          16 :         DataContainer<data_t> dc(VolumeDescriptor{{5, 5}});
    1542          16 :         dc(0, 0) = 28;
    1543          16 :         dc(0, 1) = 1;
    1544          16 :         dc(0, 2) = 5;
    1545          16 :         dc(0, 3) = -18;
    1546          16 :         dc(0, 4) = 8;
    1547          16 :         dc(1, 0) = 5;
    1548          16 :         dc(1, 1) = 6;
    1549          16 :         dc(1, 2) = 50;
    1550          16 :         dc(1, 3) = -8;
    1551          16 :         dc(1, 4) = 9;
    1552          16 :         dc(2, 0) = 8;
    1553          16 :         dc(2, 1) = 9;
    1554          16 :         dc(2, 2) = 10;
    1555          16 :         dc(2, 3) = 11;
    1556          16 :         dc(2, 4) = 12;
    1557          16 :         dc(3, 0) = -12;
    1558          16 :         dc(3, 1) = -41;
    1559          16 :         dc(3, 2) = -10;
    1560          16 :         dc(3, 3) = -9;
    1561          16 :         dc(3, 4) = -8;
    1562          16 :         dc(4, 0) = -70;
    1563          16 :         dc(4, 1) = -6;
    1564          16 :         dc(4, 2) = 22;
    1565          16 :         dc(4, 3) = -10;
    1566          16 :         dc(4, 4) = -3;
    1567             : 
    1568          16 :         DataContainer<data_t> expectedFFTShiftDC(VolumeDescriptor{{5, 5}});
    1569          16 :         expectedFFTShiftDC(0, 0) = -9;
    1570          16 :         expectedFFTShiftDC(0, 1) = -8;
    1571          16 :         expectedFFTShiftDC(0, 2) = -12;
    1572          16 :         expectedFFTShiftDC(0, 3) = -41;
    1573          16 :         expectedFFTShiftDC(0, 4) = -10;
    1574          16 :         expectedFFTShiftDC(1, 0) = -10;
    1575          16 :         expectedFFTShiftDC(1, 1) = -3;
    1576          16 :         expectedFFTShiftDC(1, 2) = -70;
    1577          16 :         expectedFFTShiftDC(1, 3) = -6;
    1578          16 :         expectedFFTShiftDC(1, 4) = 22;
    1579          16 :         expectedFFTShiftDC(2, 0) = -18;
    1580          16 :         expectedFFTShiftDC(2, 1) = 8;
    1581          16 :         expectedFFTShiftDC(2, 2) = 28;
    1582          16 :         expectedFFTShiftDC(2, 3) = 1;
    1583          16 :         expectedFFTShiftDC(2, 4) = 5;
    1584          16 :         expectedFFTShiftDC(3, 0) = -8;
    1585          16 :         expectedFFTShiftDC(3, 1) = 9;
    1586          16 :         expectedFFTShiftDC(3, 2) = 5;
    1587          16 :         expectedFFTShiftDC(3, 3) = 6;
    1588          16 :         expectedFFTShiftDC(3, 4) = 50;
    1589          16 :         expectedFFTShiftDC(4, 0) = 11;
    1590          16 :         expectedFFTShiftDC(4, 1) = 12;
    1591          16 :         expectedFFTShiftDC(4, 2) = 8;
    1592          16 :         expectedFFTShiftDC(4, 3) = 9;
    1593          16 :         expectedFFTShiftDC(4, 4) = 10;
    1594             : 
    1595          16 :         WHEN("running the FFT shift operation to the container")
    1596          16 :         {
    1597           8 :             DataContainer<data_t> fftShiftedDC = fftShift2D(dc);
    1598           8 :             THEN("the data descriptors match")
    1599           8 :             {
    1600           4 :                 REQUIRE_EQ(fftShiftedDC.getDataDescriptor(),
    1601           4 :                            expectedFFTShiftDC.getDataDescriptor());
    1602           4 :             }
    1603           8 :             THEN("the data containers match") { REQUIRE_UNARY(fftShiftedDC == expectedFFTShiftDC); }
    1604           8 :         }
    1605             : 
    1606          16 :         DataContainer<data_t> expectedIFFTShiftDC(VolumeDescriptor{{5, 5}});
    1607          16 :         expectedIFFTShiftDC(0, 0) = 10;
    1608          16 :         expectedIFFTShiftDC(0, 1) = 11;
    1609          16 :         expectedIFFTShiftDC(0, 2) = 12;
    1610          16 :         expectedIFFTShiftDC(0, 3) = 8;
    1611          16 :         expectedIFFTShiftDC(0, 4) = 9;
    1612          16 :         expectedIFFTShiftDC(1, 0) = -10;
    1613          16 :         expectedIFFTShiftDC(1, 1) = -9;
    1614          16 :         expectedIFFTShiftDC(1, 2) = -8;
    1615          16 :         expectedIFFTShiftDC(1, 3) = -12;
    1616          16 :         expectedIFFTShiftDC(1, 4) = -41;
    1617          16 :         expectedIFFTShiftDC(2, 0) = 22;
    1618          16 :         expectedIFFTShiftDC(2, 1) = -10;
    1619          16 :         expectedIFFTShiftDC(2, 2) = -3;
    1620          16 :         expectedIFFTShiftDC(2, 3) = -70;
    1621          16 :         expectedIFFTShiftDC(2, 4) = -6;
    1622          16 :         expectedIFFTShiftDC(3, 0) = 5;
    1623          16 :         expectedIFFTShiftDC(3, 1) = -18;
    1624          16 :         expectedIFFTShiftDC(3, 2) = 8;
    1625          16 :         expectedIFFTShiftDC(3, 3) = 28;
    1626          16 :         expectedIFFTShiftDC(3, 4) = 1;
    1627          16 :         expectedIFFTShiftDC(4, 0) = 50;
    1628          16 :         expectedIFFTShiftDC(4, 1) = -8;
    1629          16 :         expectedIFFTShiftDC(4, 2) = 9;
    1630          16 :         expectedIFFTShiftDC(4, 3) = 5;
    1631          16 :         expectedIFFTShiftDC(4, 4) = 6;
    1632             : 
    1633          16 :         WHEN("running the IFFT shift operation to the container")
    1634          16 :         {
    1635           8 :             DataContainer<data_t> ifftShiftedDC = ifftShift2D(dc);
    1636           8 :             THEN("the data descriptors match")
    1637           8 :             {
    1638           4 :                 REQUIRE_EQ(ifftShiftedDC.getDataDescriptor(),
    1639           4 :                            expectedIFFTShiftDC.getDataDescriptor());
    1640           4 :             }
    1641           8 :             THEN("the data containers match")
    1642           8 :             {
    1643           4 :                 REQUIRE_UNARY(ifftShiftedDC == expectedIFFTShiftDC);
    1644           4 :             }
    1645           8 :         }
    1646          16 :     }
    1647          48 : }
    1648             : 
    1649             : // "instantiate" the test templates for CPU types
    1650             : TEST_CASE_TEMPLATE_APPLY(datacontainer_construction, CPUTypeTuple);
    1651             : TEST_CASE_TEMPLATE_APPLY(datacontainer_reduction, CPUTypeTuple);
    1652             : TEST_CASE_TEMPLATE_APPLY(datacontainer_elemwise, CPUTypeTuple);
    1653             : TEST_CASE_TEMPLATE_APPLY(datacontainer_arithmetic, CPUTypeTuple);
    1654             : TEST_CASE_TEMPLATE_APPLY(datacontainer_maps, CPUTypeTuple);
    1655             : 
    1656             : #ifdef ELSA_CUDA_VECTOR
    1657             : // "instantiate" the test templates for GPU types
    1658             : TEST_CASE_TEMPLATE_APPLY(datacontainer_construction, GPUTypeTuple);
    1659             : TEST_CASE_TEMPLATE_APPLY(datacontainer_reduction, GPUTypeTuple);
    1660             : TEST_CASE_TEMPLATE_APPLY(datacontainer_elemwise, GPUTypeTuple);
    1661             : TEST_CASE_TEMPLATE_APPLY(datacontainer_arithmetic, GPUTypeTuple);
    1662             : TEST_CASE_TEMPLATE_APPLY(datacontainer_maps, GPUTypeTuple);
    1663             : #endif
    1664             : 
    1665             : TEST_SUITE_END();

Generated by: LCOV version 1.14