LCOV - code coverage report
Current view: top level - test_routines - testHelpers.h (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 0 46 0.0 %
Date: 2022-08-04 03:43:28 Functions: 0 46 0.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <type_traits>
       4             : #include <complex>
       5             : #include <random>
       6             : #include "doctest/doctest.h"
       7             : #include "elsaDefines.h"
       8             : #include "DataDescriptor.h"
       9             : #include "DataContainer.h"
      10             : 
      11             : #include <iomanip>
      12             : #include <limits>
      13             : #include <cassert>
      14             : 
      15             : namespace elsa
      16             : {
      17             :     /// With C++20 this can be replaced by std::type_identity
      18             :     template <class T>
      19             :     struct SelfType {
      20             :         using type = T;
      21             :     };
      22             : 
      23             :     template <class T>
      24             :     using SelfType_t = typename SelfType<T>::type;
      25             : 
      26             :     /**
      27             :      * @brief Epsilon (in percentage) value for our test suit
      28             :      */
      29             :     static constexpr elsa::real_t epsilon = static_cast<elsa::real_t>(0.01);
      30             : 
      31             :     /**
      32             :      * @brief literal operator to convert `long double` value to `doctest::Approx`
      33             :      */
      34             :     doctest::Approx operator"" _a(long double val);
      35             : 
      36             :     /**
      37             :      * @brief  literal operator to convert `unsigned long long` value to `doctest::Approx`
      38             :      */
      39             :     doctest::Approx operator"" _a(unsigned long long val);
      40             : 
      41             :     /**
      42             :      * @brief comparing two number types for approximate equality for complex and regular number
      43             :      *
      44             :      * Use example in test case: REQUIRE_UNARY(checkSameNumbers(a, b));
      45             :      * The CHECK(...) assertion in the function ensures that the values are reported when the test
      46             :      * fails.
      47             : 
      48             :      * @tparam T - arithmetic data type
      49             :      * @return true if same number
      50             :      *
      51             :      * @tparam T - arithmetic data type
      52             :      * @return true if same number
      53             :      */
      54             :     template <typename T>
      55             :     bool checkSameNumbers(T left, SelfType_t<T> right)
      56             :     {
      57             :         return checkApproxEq(left, right);
      58             :     }
      59             : 
      60             :     /**
      61             :      * @brief compare two numbers depending on their type approximate it.
      62             :      *
      63             :      * For complex numbers real and imaginary parts are compared separately, floating point
      64             :      * values are compared using doctest::Approx. All other types are just compared using
      65             :      * `CHECK_EQ`.
      66             :      *
      67             :      * Use example in test case: REQUIRE_UNARY(checkSameNumbers(a, b));
      68             :      * The CHECK(...) assertion in the function ensures that the values are reported when the test
      69             :      * fails.
      70             :      *
      71             :      * @tparam T - arithmetic data type
      72             :      * @param left left part of comparison, determines template type
      73             :      * @param right right part of comparison, not part of deducing template type
      74             :      * @param margin allowed tolerance in percentage
      75             :      */
      76             :     template <typename T>
      77           0 :     [[nodiscard]] bool checkApproxEq(T left, SelfType_t<T> right, double margin = epsilon)
      78             :     {
      79             :         using Approx = doctest::Approx;
      80             : 
      81             :         if constexpr (std::is_same_v<T, complex<float>> || std::is_same_v<T, complex<double>>) {
      82           0 :             CHECK_EQ(Approx(left.real()).epsilon(margin), right.real());
      83           0 :             CHECK_EQ(Approx(left.imag()).epsilon(margin), right.imag());
      84             : 
      85           0 :             return Approx(left.real()).epsilon(margin) == right.real()
      86           0 :                    && Approx(left.imag()).epsilon(margin) == right.imag();
      87             :         } else if constexpr (std::is_floating_point_v<T>) {
      88           0 :             CHECK_EQ(Approx(left).epsilon(margin), right);
      89           0 :             return Approx(left).epsilon(margin) == right;
      90             :         } else {
      91           0 :             CHECK_EQ(left, right);
      92           0 :             return left == right;
      93             :         }
      94             :     }
      95             : 
      96             :     template <typename T>
      97           0 :     [[nodiscard]] bool checkApproxNe(T left, SelfType_t<T> right, double margin = epsilon)
      98             :     {
      99             :         using Approx = doctest::Approx;
     100             : 
     101             :         if constexpr (std::is_same_v<T, complex<float>> || std::is_same_v<T, complex<double>>) {
     102           0 :             CHECK_NE(Approx(left.real()).epsilon(margin), right.real());
     103           0 :             CHECK_NE(Approx(left.imag()).epsilon(margin), right.imag());
     104             : 
     105           0 :             return Approx(left.real()).epsilon(margin) != right.real()
     106           0 :                    && Approx(left.imag()).epsilon(margin) != right.imag();
     107             :         } else if constexpr (std::is_floating_point_v<T>) {
     108           0 :             CHECK_NE(Approx(left).epsilon(margin), right);
     109           0 :             return Approx(left).epsilon(margin) != right;
     110             :         } else {
     111           0 :             CHECK_NE(left, right);
     112           0 :             return left != right;
     113             :         }
     114             :     }
     115             : 
     116             :     template <typename T>
     117           0 :     [[nodiscard]] bool approxEq(T left, SelfType_t<T> right)
     118             :     {
     119             :         using Approx = doctest::Approx;
     120             : 
     121             :         if constexpr (std::is_same_v<T, complex<float>> || std::is_same_v<T, complex<double>>) {
     122           0 :             return Approx(left.real()).epsilon(epsilon) == right.real()
     123           0 :                    && Approx(left.imag()).epsilon(epsilon) == right.imag();
     124             :         } else if constexpr (std::is_floating_point_v<T>) {
     125           0 :             return Approx(left).epsilon(epsilon) == right;
     126             :         } else {
     127           0 :             return left == right;
     128             :         }
     129             :     }
     130             : 
     131             :     /**
     132             :      * @brief Generates a random Eigen matrix for different data_t types with integer values limited
     133             :      * to a certain range
     134             :      *
     135             :      * @param[in] size the number of elements in the vector like matrix
     136             :      *
     137             :      * @tparam data_t the numerical type to use
     138             :      *
     139             :      * The integer range is chosen to be small, to allow multiplication with the values without
     140             :      * running into overflow issues.
     141             :      */
     142             :     template <typename data_t>
     143           0 :     auto generateRandomMatrix(index_t size)
     144             :     {
     145           0 :         Vector_t<data_t> randVec(size);
     146             : 
     147             :         if constexpr (std::is_integral_v<data_t>) {
     148             :             // Define range depending on signed or unsigned type
     149           0 :             const auto [rangeBegin, rangeEnd] = []() -> std::tuple<data_t, data_t> {
     150             :                 if constexpr (std::is_signed_v<data_t>) {
     151             :                     return {-100, 100};
     152             :                 } else {
     153             :                     return {1, 100};
     154             :                 }
     155             :             }();
     156             : 
     157           0 :             std::random_device rd;
     158           0 :             std::mt19937 eng(rd());
     159           0 :             std::uniform_int_distribution<data_t> distr(rangeBegin, rangeEnd);
     160             : 
     161           0 :             for (index_t i = 0; i < size; ++i) {
     162           0 :                 data_t num = distr(eng);
     163             : 
     164             :                 // remove zeros as this leads to errors when dividing
     165           0 :                 if (num == 0)
     166           0 :                     num = 1;
     167           0 :                 randVec[i] = num;
     168             :             }
     169           0 :         } else {
     170           0 :             randVec.setRandom();
     171             :         }
     172             : 
     173           0 :         return randVec;
     174           0 :     }
     175             : 
     176             :     /**
     177             :      * @brief generate a random eigen vector and a DataContainer with the same data. Specifically
     178             :      * take index_t into consideration and scale the random eigen vector, to not generate overflows
     179             :      *
     180             :      * @tparam data_t Value type of DataContainers
     181             :      * @param desc First DataContainer
     182             :      * @param handlerType Second DataContainer
     183             :      *
     184             :      * @return a pair of a DataContainer and eigen vector, of same size and the same values
     185             :      */
     186             :     template <typename data_t>
     187             :     std::tuple<DataContainer<data_t>, Vector_t<data_t>>
     188           0 :         generateRandomContainer(const DataDescriptor& desc, DataHandlerType handlerType)
     189             :     {
     190           0 :         auto containerSize = desc.getNumberOfCoefficients();
     191             : 
     192           0 :         auto randVec = generateRandomMatrix<data_t>(containerSize);
     193             : 
     194           0 :         auto dc = DataContainer<data_t>(desc, randVec, handlerType);
     195             : 
     196           0 :         return {dc, randVec};
     197           0 :     }
     198             :     /**
     199             :      * @brief Compares two DataContainers using their norm. Computes \f$ \sqrt{\| x - y \|_{2}^2}
     200             :      * \f$ and compares it to \f$ prec * \sqrt{min(\| x \|_{2}^2, \| y \|_{2}^2)} \f$. If the first
     201             :      * is smaller or equal to the second, we can assume the vectors are approximate equal
     202             :      *
     203             :      * @tparam data_t Value type of DataContainers
     204             :      * @param x First DataContainer
     205             :      * @param y Second DataContainer
     206             :      * @param prec Precision to compare, the smaller the closer both have to be
     207             :      * @return true if the norms of the containers is approximate equal
     208             :      */
     209             :     template <typename data_t>
     210             :     [[nodiscard]] bool isApprox(const DataContainer<data_t>& x, const DataContainer<data_t>& y,
     211             :                                 real_t prec = Eigen::NumTraits<real_t>::dummy_precision());
     212             : 
     213             :     template <typename data_t>
     214             :     [[nodiscard]] bool isApprox(const DataContainer<data_t>& x, const Vector_t<data_t>& y,
     215             :                                 real_t prec = Eigen::NumTraits<real_t>::dummy_precision());
     216             : 
     217             :     template <typename data_t>
     218             :     [[nodiscard]] bool isApprox(const DataHandler<data_t>& x, const DataHandler<data_t>& y,
     219             :                                 real_t prec = Eigen::NumTraits<real_t>::dummy_precision());
     220             : 
     221             :     template <typename data_t>
     222             :     [[nodiscard]] bool isApprox(const DataHandler<data_t>& x, const Vector_t<data_t>& y,
     223             :                                 real_t prec = Eigen::NumTraits<real_t>::dummy_precision());
     224             : 
     225             :     template <typename data_t, typename Source, typename = std::enable_if_t<isExpression<Source>>>
     226           0 :     [[nodiscard]] bool isApprox(const DataContainer<data_t>& x, const Source& y,
     227             :                                 real_t prec = Eigen::NumTraits<real_t>::dummy_precision())
     228             :     {
     229           0 :         return isApprox(x, DataContainer<data_t>{y}, prec);
     230             :     }
     231             : 
     232             :     template <typename data_t>
     233             :     [[nodiscard]] bool isCwiseApprox(const DataContainer<data_t>& x,
     234             :                                      const DataContainer<data_t>& y);
     235             : 
     236             :     template <typename data_t>
     237             :     [[nodiscard]] bool isCwiseApprox(const DataContainer<data_t>& x, const Vector_t<data_t>& y);
     238             : 
     239             :     template <typename data_t>
     240             :     [[nodiscard]] bool isCwiseApprox(const DataHandler<data_t>& x, const DataHandler<data_t>& y);
     241             : 
     242             :     template <typename data_t>
     243             :     [[nodiscard]] bool isCwiseApprox(const DataHandler<data_t>& x, const Vector_t<data_t>& y);
     244             : 
     245             :     /**
     246             :      * @brief Wrapper to remove const, volatile and reference of a type
     247             :      */
     248             :     template <typename T>
     249             :     using UnqualifiedType_t =
     250             :         typename std::remove_cv<typename std::remove_reference<T>::type>::type;
     251             : 
     252             :     /**
     253             :      * @brief Helper to give types a name, this is used to print information during testing
     254             :      *
     255             :      * @tparam T type that should be given a name
     256             :      * @tparam Dummy dummy, to be used to enable or disable specific specializations
     257             :      */
     258             :     template <typename T, typename Dummy = void>
     259             :     struct TypeName;
     260             : 
     261             :     /**
     262             :      * @brief specialization to specify a name for index_t
     263             :      * @tparam T [const] [volatile] index_t[&] should be accepted
     264             :      */
     265             :     template <typename T>
     266             :     struct TypeName<T, std::enable_if_t<std::is_same_v<index_t, UnqualifiedType_t<T>>>> {
     267             :         static constexpr char name[] = "index_t";
     268             :     };
     269             : 
     270             :     /**
     271             :      * @brief specialization to specify a name for float
     272             :      * @tparam T [const] [volatile] float[&] should be accepted
     273             :      */
     274             :     template <typename T>
     275             :     struct TypeName<T, std::enable_if_t<std::is_same_v<float, UnqualifiedType_t<T>>>> {
     276             :         static constexpr char name[] = "float";
     277             :     };
     278             : 
     279             :     /**
     280             :      * @brief specialization to specify a name for double
     281             :      * @tparam T [const] [volatile] double[&] should be accepted
     282             :      */
     283             :     template <typename T>
     284             :     struct TypeName<T, std::enable_if_t<std::is_same_v<double, UnqualifiedType_t<T>>>> {
     285             :         static constexpr char name[] = "double";
     286             :     };
     287             : 
     288             :     /**
     289             :      * @brief specialization to specify a name for complex<float>
     290             :      * @tparam T [const] [volatile] complex<float>[&] should be accepted
     291             :      */
     292             :     template <typename T>
     293             :     struct TypeName<T, std::enable_if_t<std::is_same_v<complex<float>, UnqualifiedType_t<T>>>> {
     294             :         static constexpr char name[] = "complex<float>";
     295             :     };
     296             : 
     297             :     /**
     298             :      * @brief specialization to specify a name for complex<double>
     299             :      * @tparam T [const] [volatile] complex<double>[&] should be accepted
     300             :      */
     301             :     template <typename T>
     302             :     struct TypeName<T, std::enable_if_t<std::is_same_v<complex<double>, UnqualifiedType_t<T>>>> {
     303             :         static constexpr char name[] = "complex<double>";
     304             :     };
     305             : 
     306             :     /**
     307             :      * @brief Quick access to TypeName<UnqualifiedType>::name
     308             :      * @tparam T a type
     309             :      */
     310             :     template <typename T>
     311             :     static constexpr auto TypeName_v = TypeName<UnqualifiedType_t<T>>::name;
     312             : } // namespace elsa

Generated by: LCOV version 1.14