LCOV - code coverage report
Current view: top level - elsa/core - DataContainer.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 607 731 83.0 %
Date: 2024-05-16 04:22:26 Functions: 568 908 62.6 %

          Line data    Source code
       1             : #include "DataContainer.h"
       2             : #include "DataContainerFormatter.hpp"
       3             : #include "FormatConfig.h"
       4             : #include "BlockDescriptor.h"
       5             : #include "IdenticalBlocksDescriptor.h"
       6             : #include "RandomBlocksDescriptor.h"
       7             : #include "PartitionDescriptor.h"
       8             : #include "Error.h"
       9             : #include "TypeCasts.hpp"
      10             : #include "Assertions.h"
      11             : 
      12             : #include "Complex.h"
      13             : 
      14             : #include "Functions.hpp"
      15             : #include "TypeTraits.hpp"
      16             : #include "elsaDefines.h"
      17             : #include "functions/Conj.hpp"
      18             : #include "functions/Imag.hpp"
      19             : #include "functions/Real.hpp"
      20             : 
      21             : #include "reductions/DotProduct.h"
      22             : #include "reductions/L0.h"
      23             : #include "reductions/L1.h"
      24             : #include "reductions/L2.h"
      25             : #include "reductions/LInf.h"
      26             : #include "reductions/Sum.h"
      27             : #include "reductions/Extrema.h"
      28             : 
      29             : #include "transforms/Absolute.h"
      30             : #include "transforms/Add.h"
      31             : #include "transforms/Assign.h"
      32             : #include "transforms/Clip.h"
      33             : #include "transforms/Cast.h"
      34             : #include "transforms/Sub.h"
      35             : #include "transforms/Div.h"
      36             : #include "transforms/Extrema.h"
      37             : #include "transforms/InplaceAdd.h"
      38             : #include "transforms/InplaceSub.h"
      39             : #include "transforms/InplaceMul.h"
      40             : #include "transforms/InplaceDiv.h"
      41             : #include "transforms/Sign.h"
      42             : #include "transforms/Square.h"
      43             : #include "transforms/Sqrt.h"
      44             : #include "transforms/Log.h"
      45             : #include "transforms/Lincomb.h"
      46             : #include "transforms/Exp.h"
      47             : #include "transforms/Imag.h"
      48             : #include "transforms/Real.h"
      49             : #include "transforms/Bessel.h"
      50             : #include "transforms/FFT.h"
      51             : 
      52             : #include <utility>
      53             : #include <cmath>
      54             : #include <algorithm>
      55             : 
      56             : namespace elsa
      57             : {
      58             : 
      59             :     template <typename data_t>
      60             :     void DataContainer<data_t>::assign(const DataContainer<data_t>& other)
      61       17271 :     {
      62       17271 :         if (this->getSize() != other.getSize()) {
      63           0 :             throw InvalidArgumentError("assign: sizes are not equal");
      64           0 :         }
      65       17271 :         elsa::assign(other.begin(), other.end(), this->begin());
      66       17271 :     }
      67             : 
      68             :     template <typename data_t>
      69             :     DataContainer<data_t>::DataContainer(const DataDescriptor& dataDescriptor)
      70             :         : _dataDescriptor{dataDescriptor.clone()},
      71             :           storage_{
      72             :               ContiguousStorage<data_t>(asUnsigned(_dataDescriptor->getNumberOfCoefficients()))}
      73       52938 :     {
      74       52938 :     }
      75             : 
      76             :     template <typename data_t>
      77             :     DataContainer<data_t>::DataContainer(const DataDescriptor& dataDescriptor,
      78             :                                          const Eigen::Matrix<data_t, Eigen::Dynamic, 1>& data)
      79             :         : _dataDescriptor{dataDescriptor.clone()},
      80             :           storage_{std::in_place_type<ContiguousStorage<data_t>>, data.begin(), data.end()}
      81        3330 :     {
      82        3330 :         if (getSize() != dataDescriptor.getNumberOfCoefficients())
      83           0 :             throw InvalidArgumentError("DataContainer: initialization vector has invalid size");
      84        3330 :     }
      85             : 
      86             :     template <typename data_t>
      87             :     DataContainer<data_t>::DataContainer(const DataDescriptor& dataDescriptor,
      88             :                                          const ContiguousStorage<data_t>& storage)
      89             :         : _dataDescriptor{dataDescriptor.clone()}, storage_{storage}
      90         628 :     {
      91         628 :     }
      92             : 
      93             :     template <typename data_t>
      94             :     DataContainer<data_t>::DataContainer(const DataDescriptor& dataDescriptor,
      95             :                                          ContiguousStorageView<data_t> span)
      96             :         : _dataDescriptor{dataDescriptor.clone()}, storage_{span}
      97      109171 :     {
      98      109171 :     }
      99             : 
     100             :     template <typename data_t>
     101             :     template <mr::StorageType tag>
     102             :     DataContainer<data_t>::DataContainer(const DataDescriptor& dataDescriptor,
     103             :                                          NdViewTagged<data_t, tag>& view)
     104             :         : _dataDescriptor{dataDescriptor.clone()}, storage_{ContiguousStorage<data_t>(0)}
     105           0 :     {
     106           0 :         static_assert(tag == mr::StorageType::device || tag == mr::StorageType::host
     107           0 :                       || tag == mr::StorageType::universal);
     108             : 
     109           0 :         if (tag == mr::StorageType::device && mr::sysStorageType == mr::StorageType::host) {
     110             :             /* :( */
     111           0 :             throw std::runtime_error("Unsupported device");
     112           0 :         }
     113             : 
     114           0 :         auto& viewShape = view.shape();
     115           0 :         size_t dimCount = viewShape.size();
     116           0 :         if (static_cast<size_t>(dataDescriptor.getNumberOfDimensions()) != dimCount) {
     117           0 :             throw std::runtime_error("Descriptor and NdView have different dimensionality");
     118           0 :         }
     119           0 :         auto descriptorShape = dataDescriptor.getNumberOfCoefficientsPerDimension();
     120           0 :         for (size_t i = 0; i < dimCount; i++) {
     121           0 :             if (viewShape(i) != descriptorShape(i)) {
     122           0 :                 throw std::runtime_error(std::string("Mismatch in Descriptor and NdView dimension ")
     123           0 :                                          + std::to_string(i));
     124           0 :             }
     125           0 :         }
     126             : 
     127           0 :         ContiguousStorage<data_t>& storage = std::get<ContiguousStorage<data_t>>(storage_);
     128             : 
     129           0 :         if (tag == mr::sysStorageType && view.is_canonical()) {
     130           0 :             auto sharedCleanupRoutine = view.getCleanup();
     131           0 :             struct {
     132           0 :                 decltype(sharedCleanupRoutine) cleanup;
     133           0 :                 void operator()()
     134           0 :                 {
     135             :                     /* drop reference to cleanup;
     136             :                        if it reaches 0, cleanup is performed */
     137           0 :                     cleanup.reset();
     138           0 :                 }
     139           0 :             } cleanupFunctor = {sharedCleanupRoutine};
     140             : 
     141           0 :             auto range = view.canonical_range();
     142           0 :             storage.from_extern(thrust::raw_pointer_cast(range.begin()),
     143           0 :                                 range.end() - range.begin(), cleanupFunctor);
     144           0 :             return;
     145           0 :         }
     146             : 
     147           0 :         auto range = view.range();
     148           0 :         storage.assign(range.begin(), range.end());
     149           0 :     }
     150             : 
     151             :     template <typename data_t>
     152             :     DataContainer<data_t>::DataContainer(const DataContainer<data_t>& other)
     153             :         : _dataDescriptor{other._dataDescriptor->clone()}, storage_{other.storage_}
     154        7564 :     {
     155        7564 :     }
     156             : 
     157             :     template <typename data_t>
     158             :     DataContainer<add_complex_t<data_t>> DataContainer<data_t>::asComplex() const
     159         572 :     {
     160         572 :         return elsa::asComplex(*this);
     161         572 :     }
     162             : 
     163             :     template <typename data_t>
     164             :     DataContainer<data_t>& DataContainer<data_t>::operator=(const DataContainer<data_t>& other)
     165        8009 :     {
     166        8009 :         if (this != &other) {
     167        7767 :             _dataDescriptor = other._dataDescriptor->clone();
     168             : 
     169             :             // Assign the values from other to this storage, if this is a view, this will not
     170             :             // reallocate, but write through to the original data container
     171        7767 :             std::visit(
     172        7767 :                 overloaded{
     173        7767 :                     [](auto& self, const auto other) { self.assign(other.begin(), other.end()); },
     174        7767 :                 },
     175        7767 :                 storage_, other.storage_);
     176        7767 :         }
     177             : 
     178        8009 :         return *this;
     179        8009 :     }
     180             : 
     181             :     template <typename data_t>
     182             :     DataContainer<data_t>::DataContainer(DataContainer<data_t>&& other) noexcept
     183             :         : _dataDescriptor{std::move(other._dataDescriptor)}, storage_{std::move(other.storage_)}
     184       36449 :     {
     185       36449 :     }
     186             : 
     187             :     template <typename data_t>
     188             :     DataContainer<data_t>& DataContainer<data_t>::operator=(DataContainer<data_t>&& other) noexcept
     189       13832 :     {
     190       13832 :         _dataDescriptor = std::move(other._dataDescriptor);
     191             : 
     192             :         // If this is a view, we need to write the values from other into this, this is due to the
     193             :         // dual requirement of data containers to be owning and views
     194       13832 :         if (isView()) {
     195         640 :             std::visit(
     196         640 :                 overloaded{
     197         640 :                     [](auto& self, const auto other) { self.assign(other.begin(), other.end()); },
     198         640 :                 },
     199         640 :                 storage_, other.storage_);
     200             : 
     201       13192 :         } else {
     202       13192 :             storage_ = std::move(other.storage_);
     203       13192 :         }
     204             : 
     205       13832 :         return *this;
     206       13832 :     }
     207             : 
     208             :     template <typename data_t>
     209             :     DataContainer<data_t> DataContainer<data_t>::fromRawData(
     210             :         data_t* raw_data, mr::StorageType storageType, const IndexVector_t& shape,
     211             :         const IndexVector_t& strides, const DataDescriptor& desc, std::function<void()> destructor)
     212           0 :     {
     213           0 :         switch (storageType) {
     214           0 :             case mr::StorageType::host: {
     215           0 :                 NdViewTagged<data_t, mr::StorageType::host> view(raw_data, shape, strides,
     216           0 :                                                                  destructor);
     217           0 :                 return DataContainer(desc, view);
     218           0 :             }
     219           0 :             case mr::StorageType::device: {
     220           0 :                 NdViewTagged<data_t, mr::StorageType::device> view(raw_data, shape, strides,
     221           0 :                                                                    destructor);
     222           0 :                 return DataContainer(desc, view);
     223           0 :             }
     224           0 :             case mr::StorageType::universal: {
     225           0 :                 NdViewTagged<data_t, mr::StorageType::universal> view(raw_data, shape, strides,
     226           0 :                                                                       destructor);
     227           0 :                 return DataContainer(desc, view);
     228           0 :             }
     229           0 :         }
     230           0 :         throw Error("Unknown storage type!");
     231           0 :     }
     232             : 
     233             :     template <typename data_t>
     234             :     NdView<data_t> DataContainer<data_t>::toNdView() const
     235           1 :     {
     236           1 :         auto& desc = getDataDescriptor();
     237           1 :         const auto& shape_vector = desc.getNumberOfCoefficientsPerDimension();
     238           1 :         const auto& strides_vector = desc.getProductOfCoefficientsPerDimension();
     239             : 
     240           1 :         elsa::mr::NativeContainer<data_t> data = storage().lock_native();
     241           1 :         return NdView<data_t>(data.raw_pointer, shape_vector, strides_vector, data.release);
     242           1 :     }
     243             : 
     244             :     template <typename data_t>
     245             :     const DataDescriptor& DataContainer<data_t>::getDataDescriptor() const
     246    26227736 :     {
     247    26227736 :         return *_dataDescriptor;
     248    26227736 :     }
     249             : 
     250             :     template <typename data_t>
     251             :     bool DataContainer<data_t>::isOwning() const
     252         710 :     {
     253         710 :         return std::visit(
     254         710 :             overloaded{[](const auto& storage) {
     255         710 :                 return std::is_same_v<std::decay_t<decltype(storage)>, ContiguousStorage<data_t>>;
     256         710 :             }},
     257         710 :             storage_);
     258         710 :     }
     259             : 
     260             :     template <typename data_t>
     261             :     bool DataContainer<data_t>::isView() const
     262       13832 :     {
     263       13832 :         return std::visit(overloaded{[](const auto& storage) {
     264       13832 :                               return std::is_same_v<std::decay_t<decltype(storage)>,
     265       13832 :                                                     ContiguousStorageView<data_t>>;
     266       13832 :                           }},
     267       13832 :                           storage_);
     268       13832 :     }
     269             : 
     270             :     template <typename data_t>
     271             :     const ContiguousStorage<data_t>& DataContainer<data_t>::storage() const
     272        1614 :     {
     273        1614 :         using RetRef = const ContiguousStorage<data_t>&;
     274        1614 :         return std::visit(
     275        1614 :             overloaded{
     276        1614 :                 [](const ContiguousStorage<data_t>& storage) -> RetRef { return storage; },
     277        1614 :                 [](ContiguousStorageView<data_t> storage) -> RetRef { return storage.storage(); }},
     278        1614 :             storage_);
     279        1614 :     }
     280             : 
     281             :     template <typename data_t>
     282             :     ContiguousStorage<data_t>& DataContainer<data_t>::storage()
     283         261 :     {
     284         261 :         using RetRef = ContiguousStorage<data_t>&;
     285         261 :         return std::visit(
     286         261 :             overloaded{
     287         261 :                 [](ContiguousStorage<data_t>& storage) -> RetRef { return storage; },
     288         261 :                 [](ContiguousStorageView<data_t> storage) -> RetRef { return storage.storage(); }},
     289         261 :             storage_);
     290         261 :     }
     291             : 
     292             :     template <typename data_t>
     293             :     index_t DataContainer<data_t>::getSize() const
     294    49143843 :     {
     295    49143843 :         return std::visit(
     296    49143843 :             overloaded{
     297    49157319 :                 [](const auto& storage) { return asSigned(storage.size()); },
     298    49143843 :             },
     299    49143843 :             storage_);
     300    49143843 :     }
     301             : 
     302             :     template <typename data_t>
     303             :     index_t DataContainer<data_t>::getNumberOfBlocks() const
     304         476 :     {
     305         476 :         if (!is<BlockDescriptor>(getDataDescriptor())) {
     306           0 :             return 1;
     307           0 :         }
     308             : 
     309         476 :         auto& blockDesc = downcast_safe<BlockDescriptor>(getDataDescriptor());
     310         476 :         return blockDesc.getNumberOfBlocks();
     311         476 :     }
     312             : 
     313             :     template <typename data_t>
     314             :     typename DataContainer<data_t>::reference DataContainer<data_t>::operator[](index_t index)
     315    28761535 :     {
     316    28761535 :         ELSA_VERIFY(index >= 0);
     317    28761535 :         ELSA_VERIFY(index < getSize());
     318             : 
     319    28761535 :         return std::visit(
     320    28761535 :             overloaded{
     321    28792310 :                 [index](auto& storage) -> reference { return storage[asUnsigned(index)]; },
     322    28761535 :             },
     323    28761535 :             storage_);
     324    28761535 :     }
     325             : 
     326             :     template <typename data_t>
     327             :     typename DataContainer<data_t>::const_reference
     328             :         DataContainer<data_t>::operator[](index_t index) const
     329    19753048 :     {
     330             :         // ELSA_VERIFY(index >= 0);
     331             :         // ELSA_VERIFY(index < getSize());
     332             : 
     333    19753048 :         return std::visit(
     334    19753048 :             overloaded{
     335    19819051 :                 [index](const auto& storage) -> const_reference {
     336    19819051 :                     return storage[asUnsigned(index)];
     337    19819051 :                 },
     338    19753048 :             },
     339    19753048 :             storage_);
     340    19753048 :     }
     341             : 
     342             :     template <typename data_t>
     343             :     data_t DataContainer<data_t>::at(const IndexVector_t& coordinate) const
     344           0 :     {
     345           0 :         const auto arr = coordinate.array();
     346           0 :         if ((arr < 0).any()
     347           0 :             || (arr >= _dataDescriptor->getNumberOfCoefficientsPerDimension().array()).any()) {
     348           0 :             return 0;
     349           0 :         }
     350             : 
     351           0 :         return (*this)[_dataDescriptor->getIndexFromCoordinate(coordinate)];
     352           0 :     }
     353             : 
     354             :     template <typename data_t>
     355             :     typename DataContainer<data_t>::reference
     356             :         DataContainer<data_t>::operator()(const IndexVector_t& coordinate)
     357     1018248 :     {
     358             :         // const auto arr = coordinate.array();
     359             :         // const auto shape = _dataDescriptor->getNumberOfCoefficientsPerDimension().array();
     360             :         // ELSA_VERIFY((arr >= 0).all());
     361             :         // ELSA_VERIFY((arr < shape).all());
     362             : 
     363     1018248 :         return (*this)[_dataDescriptor->getIndexFromCoordinate(coordinate)];
     364     1018248 :     }
     365             : 
     366             :     template <typename data_t>
     367             :     typename DataContainer<data_t>::const_reference
     368             :         DataContainer<data_t>::operator()(const IndexVector_t& coordinate) const
     369      123052 :     {
     370             :         // const auto arr = coordinate.array();
     371             :         // const auto shape = _dataDescriptor->getNumberOfCoefficientsPerDimension().array();
     372             :         // ELSA_VERIFY((arr >= 0).all());
     373             :         // ELSA_VERIFY((arr < shape).all());
     374             : 
     375      123052 :         return (*this)[_dataDescriptor->getIndexFromCoordinate(coordinate)];
     376      123052 :     }
     377             : 
     378             :     template <typename data_t>
     379             :     data_t DataContainer<data_t>::dot(const DataContainer<data_t>& other) const
     380        2002 :     {
     381        2002 :         return elsa::dot(begin(), end(), other.begin());
     382        2002 :     }
     383             : 
     384             :     template <typename data_t>
     385             :     GetFloatingPointType_t<data_t> DataContainer<data_t>::squaredL2Norm() const
     386        5187 :     {
     387        5187 :         return elsa::squaredL2Norm(begin(), end());
     388        5187 :     }
     389             : 
     390             :     template <typename data_t>
     391             :     GetFloatingPointType_t<data_t> DataContainer<data_t>::l2Norm() const
     392        2267 :     {
     393        2267 :         return elsa::l2Norm(begin(), end());
     394        2267 :     }
     395             : 
     396             :     template <typename data_t>
     397             :     DataContainer<GetFloatingPointType_t<data_t>> DataContainer<data_t>::pL2Norm() const
     398          20 :     {
     399          20 :         if (!is<IdenticalBlocksDescriptor>(getDataDescriptor())) {
     400           0 :             throw Error("pL2Norm: Descriptor must be of type IdenticalBlocksDescriptor");
     401           0 :         }
     402             : 
     403             :         // Create temporary to hold the running sum of each "column"
     404          20 :         auto tmp = DataContainer<GetFloatingPointType_t<data_t>>(getBlock(0).getDataDescriptor());
     405          20 :         tmp = 0;
     406             : 
     407          64 :         for (int i = 0; i < getNumberOfBlocks(); ++i) {
     408          44 :             if constexpr (isComplex<data_t>) {
     409          44 :                 tmp += ::elsa::square(elsa::cwiseAbs(getBlock(i)));
     410          44 :             } else {
     411          44 :                 tmp += ::elsa::square(getBlock(i));
     412          44 :             }
     413          44 :         }
     414             : 
     415          20 :         return ::elsa::sqrt(tmp);
     416          20 :     }
     417             : 
     418             :     template <typename data_t>
     419             :     index_t DataContainer<data_t>::l0PseudoNorm() const
     420           7 :     {
     421           7 :         return elsa::l0PseudoNorm(begin(), end());
     422           7 :     }
     423             : 
     424             :     template <typename data_t>
     425             :     GetFloatingPointType_t<data_t> DataContainer<data_t>::l1Norm() const
     426          83 :     {
     427          83 :         return elsa::l1Norm(begin(), end());
     428          83 :     }
     429             : 
     430             :     template <typename data_t>
     431             :     DataContainer<GetFloatingPointType_t<data_t>> DataContainer<data_t>::pL1Norm() const
     432           0 :     {
     433           0 :         if (!is<IdenticalBlocksDescriptor>(getDataDescriptor())) {
     434           0 :             throw Error("pL1Norm: Descriptor must be of type IdenticalBlocksDescriptor");
     435           0 :         }
     436             : 
     437             :         // Create temporary to hold the running sum of each "column"
     438           0 :         auto tmp = DataContainer<GetFloatingPointType_t<data_t>>(getBlock(0).getDataDescriptor());
     439           0 :         tmp = 0;
     440             : 
     441           0 :         for (int i = 0; i < getNumberOfBlocks(); ++i) {
     442           0 :             tmp += ::elsa::cwiseAbs(getBlock(i));
     443           0 :         }
     444             : 
     445           0 :         return tmp;
     446           0 :     }
     447             : 
     448             :     template <typename data_t>
     449             :     GetFloatingPointType_t<data_t> DataContainer<data_t>::lInfNorm() const
     450           9 :     {
     451           9 :         return elsa::lInf(begin(), end());
     452           9 :     }
     453             : 
     454             :     template <typename data_t>
     455             :     data_t DataContainer<data_t>::l21MixedNorm() const
     456          14 :     {
     457          14 :         return l21SmoothMixedNorm(0);
     458          14 :     }
     459             : 
     460             :     template <typename data_t>
     461             :     data_t DataContainer<data_t>::l21SmoothMixedNorm(data_t epsilon) const
     462          16 :     {
     463          16 :         auto tmp = DataContainer(this->getBlock(0).getDataDescriptor());
     464          16 :         tmp = 0;
     465             : 
     466          56 :         for (index_t i = 0; i < this->getNumberOfBlocks(); ++i) {
     467          40 :             tmp += (square(this->getBlock(i)));
     468          40 :         }
     469             : 
     470          16 :         tmp += (epsilon * epsilon);
     471             : 
     472          16 :         return sqrt(tmp).l1Norm();
     473          16 :     }
     474             : 
     475             :     template <typename data_t>
     476             :     data_t DataContainer<data_t>::sum() const
     477          59 :     {
     478          59 :         return elsa::sum(begin(), end());
     479          59 :     }
     480             : 
     481             :     template <typename data_t>
     482             :     data_t DataContainer<data_t>::minElement() const
     483          22 :     {
     484          22 :         return elsa::minElement(begin(), end());
     485          22 :     }
     486             : 
     487             :     template <typename data_t>
     488             :     data_t DataContainer<data_t>::maxElement() const
     489          26 :     {
     490          26 :         return elsa::maxElement(begin(), end());
     491          26 :     }
     492             : 
     493             :     template <typename data_t>
     494             :     void DataContainer<data_t>::fft(FFTNorm norm, FFTPolicy policy)
     495         346 :     {
     496         346 :         std::visit(overloaded{[&](ContiguousStorage<data_t>& storage) {
     497         346 :                                   elsa::fft(storage, *_dataDescriptor, norm, policy);
     498         346 :                               },
     499         346 :                               [&](ContiguousStorageView<data_t>& storage) {
     500           0 :                                   elsa::fft(storage.storage(), *_dataDescriptor, norm, policy);
     501           0 :                               }},
     502         346 :                    storage_);
     503         346 :     }
     504             : 
     505             :     template <typename data_t>
     506             :     void DataContainer<data_t>::ifft(FFTNorm norm, FFTPolicy policy)
     507         450 :     {
     508         450 :         std::visit(overloaded{[&](ContiguousStorage<data_t>& storage) {
     509         450 :                                   elsa::ifft(storage, *_dataDescriptor, norm, policy);
     510         450 :                               },
     511         450 :                               [&](ContiguousStorageView<data_t>& storage) {
     512           0 :                                   elsa::ifft(storage.storage(), *_dataDescriptor, norm, policy);
     513           0 :                               }},
     514         450 :                    storage_);
     515         450 :     }
     516             : 
     517             :     template <typename data_t>
     518             :     DataContainer<data_t>& DataContainer<data_t>::zero() &
     519           0 :     {
     520           0 :         return fill(0);
     521           0 :     }
     522             : 
     523             :     template <typename data_t>
     524             :     DataContainer<data_t> DataContainer<data_t>::zero() &&
     525           8 :     {
     526           8 :         return std::move(fill(0));
     527           8 :     }
     528             : 
     529             :     template <typename data_t>
     530             :     DataContainer<data_t>& DataContainer<data_t>::one() &
     531           0 :     {
     532           0 :         return fill(1);
     533           0 :     }
     534             : 
     535             :     template <typename data_t>
     536             :     DataContainer<data_t> DataContainer<data_t>::one() &&
     537          12 :     {
     538          12 :         return std::move(fill(1));
     539          12 :     }
     540             : 
     541             :     template <typename data_t>
     542             :     DataContainer<data_t>& DataContainer<data_t>::fill(SelfType_t<data_t> value) &
     543          24 :     {
     544          24 :         *this = value;
     545          24 :         return *this;
     546          24 :     }
     547             : 
     548             :     template <typename data_t>
     549             :     DataContainer<data_t> DataContainer<data_t>::fill(SelfType_t<data_t> value) &&
     550       34264 :     {
     551       34264 :         *this = value;
     552       34264 :         return std::move(*this);
     553       34264 :     }
     554             : 
     555             :     template <typename data_t>
     556             :     DataContainer<data_t>& DataContainer<data_t>::operator+=(const DataContainer<data_t>& dc)
     557        3118 :     {
     558        3118 :         elsa::inplaceAdd(begin(), end(), dc.begin());
     559        3118 :         return *this;
     560        3118 :     }
     561             : 
     562             :     template <typename data_t>
     563             :     DataContainer<data_t>& DataContainer<data_t>::operator-=(const DataContainer<data_t>& dc)
     564        6333 :     {
     565        6333 :         elsa::inplaceSub(begin(), end(), dc.begin());
     566        6333 :         return *this;
     567        6333 :     }
     568             : 
     569             :     template <typename data_t>
     570             :     DataContainer<data_t>& DataContainer<data_t>::operator*=(const DataContainer<data_t>& dc)
     571       10246 :     {
     572       10246 :         elsa::inplaceMul(begin(), end(), dc.begin());
     573       10246 :         return *this;
     574       10246 :     }
     575             : 
     576             :     template <typename data_t>
     577             :     DataContainer<data_t>& DataContainer<data_t>::operator/=(const DataContainer<data_t>& dc)
     578          17 :     {
     579          17 :         elsa::inplaceDiv(begin(), end(), dc.begin());
     580          17 :         return *this;
     581          17 :     }
     582             : 
     583             :     template <typename data_t>
     584             :     DataContainer<data_t>& DataContainer<data_t>::operator+=(data_t scalar)
     585          41 :     {
     586          41 :         elsa::inplaceAddScalar(begin(), end(), scalar);
     587          41 :         return *this;
     588          41 :     }
     589             : 
     590             :     template <typename data_t>
     591             :     DataContainer<data_t>& DataContainer<data_t>::operator-=(data_t scalar)
     592           9 :     {
     593           9 :         elsa::inplaceSubScalar(begin(), end(), scalar);
     594           9 :         return *this;
     595           9 :     }
     596             : 
     597             :     template <typename data_t>
     598             :     DataContainer<data_t>& DataContainer<data_t>::operator*=(data_t scalar)
     599        5790 :     {
     600        5790 :         elsa::inplaceMulScalar(begin(), end(), scalar);
     601        5790 :         return *this;
     602        5790 :     }
     603             : 
     604             :     template <typename data_t>
     605             :     DataContainer<data_t>& DataContainer<data_t>::operator/=(data_t scalar)
     606         332 :     {
     607         332 :         elsa::inplaceDivScalar(begin(), end(), scalar);
     608         332 :         return *this;
     609         332 :     }
     610             : 
     611             :     template <typename data_t>
     612             :     DataContainer<data_t>& DataContainer<data_t>::operator=(data_t scalar)
     613       56384 :     {
     614       56384 :         elsa::fill(begin(), end(), scalar);
     615       56384 :         return *this;
     616       56384 :     }
     617             : 
     618             :     template <typename data_t>
     619             :     bool DataContainer<data_t>::operator==(const DataContainer<data_t>& other) const
     620        1496 :     {
     621        1496 :         if (*_dataDescriptor != *other._dataDescriptor)
     622           0 :             return false;
     623             : 
     624             :         // if (*_dataHandler != *other._dataHandler)
     625             :         //     return false;
     626             : 
     627        1496 :         return true;
     628        1496 :     }
     629             : 
     630             :     template <typename data_t>
     631             :     bool DataContainer<data_t>::operator!=(const DataContainer<data_t>& other) const
     632          94 :     {
     633          94 :         return !(*this == other);
     634          94 :     }
     635             : 
     636             :     template <typename data_t>
     637             :     DataContainer<data_t> DataContainer<data_t>::getBlock(index_t i)
     638       71712 :     {
     639       71712 :         const auto blockDesc = downcast_safe<BlockDescriptor>(_dataDescriptor.get());
     640       71712 :         if (!blockDesc)
     641           5 :             throw LogicError("DataContainer: cannot get block from not-blocked container");
     642             : 
     643       71707 :         if (i >= blockDesc->getNumberOfBlocks() || i < 0)
     644           0 :             throw InvalidArgumentError("DataContainer: block index out of bounds");
     645             : 
     646       71707 :         size_t startIndex = asUnsigned(blockDesc->getOffsetOfBlock(i));
     647       71707 :         const auto& ithDesc = blockDesc->getDescriptorOfBlock(i);
     648       71707 :         size_t blockSize = asUnsigned(ithDesc.getNumberOfCoefficients());
     649             : 
     650       71707 :         return std::visit(overloaded{[&](ContiguousStorage<data_t>& storage) {
     651       35697 :                                          auto span = ContiguousStorageView<data_t>(
     652       35697 :                                              storage, startIndex, startIndex + blockSize);
     653       35697 :                                          return DataContainer<data_t>{ithDesc, span};
     654       35697 :                                      },
     655       71707 :                                      [&](ContiguousStorageView<data_t> storage) {
     656       35789 :                                          auto span = ContiguousStorageView<data_t>(
     657       35789 :                                              storage.storage(), storage.offset() + startIndex,
     658       35789 :                                              storage.offset() + startIndex + blockSize);
     659       35789 :                                          return DataContainer<data_t>{ithDesc, span};
     660       35789 :                                      }},
     661       71707 :                           storage_);
     662       71707 :     }
     663             : 
     664             :     template <typename data_t>
     665             :     const DataContainer<data_t> DataContainer<data_t>::getBlock(index_t i) const
     666        1044 :     {
     667        1044 :         const auto blockDesc = downcast_safe<BlockDescriptor>(_dataDescriptor.get());
     668        1044 :         if (!blockDesc)
     669           5 :             throw LogicError("DataContainer: cannot get block from not-blocked container");
     670             : 
     671        1039 :         if (i >= blockDesc->getNumberOfBlocks() || i < 0)
     672           0 :             throw InvalidArgumentError("DataContainer: block index out of bounds");
     673             : 
     674        1039 :         size_t startIndex = asUnsigned(blockDesc->getOffsetOfBlock(i));
     675        1039 :         const auto& ithDesc = blockDesc->getDescriptorOfBlock(i);
     676        1039 :         size_t blockSize = asUnsigned(ithDesc.getNumberOfCoefficients());
     677             : 
     678        1039 :         return std::visit(overloaded{[&](const ContiguousStorage<data_t>& storage) {
     679         385 :                                          auto span = ContiguousStorageView<data_t>(
     680             :                                              // Casting const away is okay, as we return a const
     681             :                                              // container
     682         385 :                                              const_cast<ContiguousStorage<data_t>&>(storage),
     683         385 :                                              startIndex, startIndex + blockSize);
     684         385 :                                          return DataContainer<data_t>{ithDesc, span};
     685         385 :                                      },
     686        1039 :                                      [&](ContiguousStorageView<data_t> storage) {
     687         654 :                                          auto span = ContiguousStorageView<data_t>(
     688         654 :                                              storage.storage(), storage.offset() + startIndex,
     689         654 :                                              storage.offset() + startIndex + blockSize);
     690         654 :                                          return DataContainer<data_t>{ithDesc, span};
     691         654 :                                      }},
     692        1039 :                           storage_);
     693        1039 :     }
     694             : 
     695             :     template <typename data_t>
     696             :     DataContainer<data_t> DataContainer<data_t>::viewAs(const DataDescriptor& dataDescriptor)
     697       35888 :     {
     698       35888 :         if (dataDescriptor.getNumberOfCoefficients() != getSize())
     699           0 :             throw InvalidArgumentError("DataContainer: view must have same size as container");
     700             : 
     701       35888 :         return std::visit(overloaded{[&](ContiguousStorage<data_t>& storage) {
     702        1224 :                                          auto span = ContiguousStorageView<data_t>(
     703        1224 :                                              storage, 0,
     704        1224 :                                              asUnsigned(dataDescriptor.getNumberOfCoefficients()));
     705        1224 :                                          return DataContainer<data_t>{dataDescriptor, span};
     706        1224 :                                      },
     707       35888 :                                      [&](ContiguousStorageView<data_t> storage) {
     708       34473 :                                          return DataContainer<data_t>{dataDescriptor, storage};
     709       34473 :                                      }},
     710       35888 :                           storage_);
     711       35888 :     }
     712             : 
     713             :     template <typename data_t>
     714             :     const DataContainer<data_t>
     715             :         DataContainer<data_t>::viewAs(const DataDescriptor& dataDescriptor) const
     716         938 :     {
     717         938 :         if (dataDescriptor.getNumberOfCoefficients() != getSize())
     718           0 :             throw InvalidArgumentError("DataContainer: view must have same size as container");
     719             : 
     720         938 :         return std::visit(overloaded{[&](const ContiguousStorage<data_t>& storage) {
     721         816 :                                          auto span = ContiguousStorageView<data_t>(
     722         816 :                                              const_cast<ContiguousStorage<data_t>&>(storage), 0,
     723         816 :                                              asUnsigned(dataDescriptor.getNumberOfCoefficients()));
     724         816 :                                          return DataContainer<data_t>{dataDescriptor, span};
     725         816 :                                      },
     726         938 :                                      [&](ContiguousStorageView<data_t> storage) {
     727         122 :                                          return DataContainer<data_t>{dataDescriptor, storage};
     728         122 :                                      }},
     729         938 :                           storage_);
     730         938 :     }
     731             : 
     732             :     template <typename data_t>
     733             :     const DataContainer<data_t> DataContainer<data_t>::slice(index_t i) const
     734         666 :     {
     735         666 :         auto& desc = getDataDescriptor();
     736         666 :         auto dim = desc.getNumberOfDimensions();
     737         666 :         auto sizeOfLastDim = desc.getNumberOfCoefficientsPerDimension()[dim - 1];
     738             : 
     739         666 :         if (i >= sizeOfLastDim) {
     740           8 :             throw LogicError("Trying to access out of bound slice");
     741           8 :         }
     742             : 
     743         658 :         if (sizeOfLastDim == 1) {
     744           4 :             return *this;
     745           4 :         }
     746             : 
     747         654 :         auto sliceDesc = PartitionDescriptor(desc, sizeOfLastDim);
     748             : 
     749             :         // Now set the slice
     750         654 :         return viewAs(sliceDesc).getBlock(i);
     751         654 :     }
     752             : 
     753             :     template <typename data_t>
     754             :     DataContainer<data_t> DataContainer<data_t>::slice(index_t i)
     755       35368 :     {
     756       35368 :         auto& desc = getDataDescriptor();
     757       35368 :         auto dim = desc.getNumberOfDimensions();
     758       35368 :         auto sizeOfLastDim = desc.getNumberOfCoefficientsPerDimension()[dim - 1];
     759             : 
     760       35368 :         if (i >= sizeOfLastDim) {
     761           4 :             throw LogicError("Trying to access out of bound slice");
     762           4 :         }
     763             : 
     764       35364 :         if (sizeOfLastDim == 1) {
     765           4 :             return *this;
     766           4 :         }
     767             : 
     768       35360 :         auto sliceDesc = PartitionDescriptor(desc, sizeOfLastDim);
     769             : 
     770             :         // Now set the slice
     771       35360 :         return viewAs(sliceDesc).getBlock(i);
     772       35360 :     }
     773             : 
     774             :     template <typename data_t>
     775             :     typename DataContainer<data_t>::iterator DataContainer<data_t>::begin()
     776      108536 :     {
     777      108536 :         return std::visit(
     778      108536 :             overloaded{
     779      108536 :                 [](auto& storage) { return storage.begin(); },
     780      108536 :             },
     781      108536 :             storage_);
     782      108536 :     }
     783             : 
     784             :     template <typename data_t>
     785             :     typename DataContainer<data_t>::const_iterator DataContainer<data_t>::begin() const
     786       65601 :     {
     787       65601 :         return cbegin();
     788       65601 :     }
     789             : 
     790             :     template <typename data_t>
     791             :     typename DataContainer<data_t>::const_iterator DataContainer<data_t>::cbegin() const
     792       65607 :     {
     793       65607 :         return std::visit(
     794       65607 :             overloaded{
     795       65607 :                 [](const auto& storage) { return storage.cbegin(); },
     796       65607 :             },
     797       65607 :             storage_);
     798       65607 :     }
     799             : 
     800             :     template <typename data_t>
     801             :     typename DataContainer<data_t>::iterator DataContainer<data_t>::end()
     802       82266 :     {
     803       82266 :         return std::visit(
     804       82266 :             overloaded{
     805       82266 :                 [](auto& storage) { return storage.end(); },
     806       82266 :             },
     807       82266 :             storage_);
     808       82266 :     }
     809             : 
     810             :     template <typename data_t>
     811             :     typename DataContainer<data_t>::const_iterator DataContainer<data_t>::end() const
     812       36850 :     {
     813       36850 :         return cend();
     814       36850 :     }
     815             : 
     816             :     template <typename data_t>
     817             :     typename DataContainer<data_t>::const_iterator DataContainer<data_t>::cend() const
     818       36856 :     {
     819       36856 :         return std::visit(
     820       36856 :             overloaded{
     821       36856 :                 [](const auto& storage) { return storage.cend(); },
     822       36856 :             },
     823       36856 :             storage_);
     824       36856 :     }
     825             : 
     826             :     template <typename data_t>
     827             :     void DataContainer<data_t>::format(std::ostream& os, format_config cfg) const
     828           0 :     {
     829           0 :         DataContainerFormatter<data_t> fmt{cfg};
     830           0 :         fmt.format(os, *this);
     831           0 :     }
     832             : 
     833             :     template <typename data_t>
     834             :     DataContainer<data_t> concatenate(const DataContainer<data_t>& dc1,
     835             :                                       const DataContainer<data_t>& dc2)
     836          28 :     {
     837          28 :         auto desc1 = dc1.getDataDescriptor().clone();
     838          28 :         auto desc2 = dc2.getDataDescriptor().clone();
     839             : 
     840          28 :         if (desc1->getNumberOfDimensions() != desc2->getNumberOfDimensions()) {
     841           4 :             throw LogicError("Can't concatenate two DataContainers with different dimension");
     842           4 :         }
     843             : 
     844          24 :         std::vector<std::unique_ptr<DataDescriptor>> descriptors;
     845          24 :         descriptors.reserve(2);
     846          24 :         descriptors.push_back(std::move(desc1));
     847          24 :         descriptors.push_back(std::move(desc2));
     848             : 
     849          24 :         auto blockDesc = RandomBlocksDescriptor(descriptors);
     850          24 :         auto concatenated = DataContainer<data_t>(blockDesc);
     851             : 
     852          24 :         concatenated.getBlock(0) = dc1;
     853          24 :         concatenated.getBlock(1) = dc2;
     854          24 :         return concatenated;
     855          24 :     }
     856             : 
     857             :     template <typename data_t>
     858             :     [[nodiscard]] DataContainer<data_t> fftShift(const DataContainer<data_t>& dc)
     859           0 :     {
     860           0 :         const DataDescriptor& desc = dc.getDataDescriptor();
     861           0 :         IndexVector_t numOfCoeffsPerDim = desc.getNumberOfCoefficientsPerDimension();
     862             : 
     863           0 :         IndexVector_t midPoint = numOfCoeffsPerDim / 2;
     864             : 
     865           0 :         DataContainer<data_t> copy{desc};
     866           0 :         for (index_t i = 0; i < desc.getNumberOfCoefficients(); ++i) {
     867           0 :             IndexVector_t idx = desc.getCoordinateFromIndex(i);
     868           0 :             IndexVector_t shifted{idx};
     869           0 :             for (index_t j = 0; j < desc.getNumberOfDimensions(); ++j) {
     870           0 :                 shifted[j] = (idx[j] + midPoint[j]) % numOfCoeffsPerDim[j];
     871           0 :             }
     872           0 :             copy(shifted) = dc(idx);
     873           0 :         }
     874           0 :         return copy;
     875           0 :     }
     876             : 
     877             :     template <typename data_t>
     878             :     DataContainer<data_t> ifftShift(const DataContainer<data_t>& dc)
     879           5 :     {
     880           5 :         const DataDescriptor& desc = dc.getDataDescriptor();
     881           5 :         IndexVector_t numOfCoeffsPerDim = desc.getNumberOfCoefficientsPerDimension();
     882             : 
     883           5 :         IndexVector_t midPoint = -numOfCoeffsPerDim / 2;
     884             : 
     885           5 :         DataContainer<data_t> copy{desc};
     886          95 :         for (index_t i = 0; i < desc.getNumberOfCoefficients(); ++i) {
     887          90 :             IndexVector_t idx = desc.getCoordinateFromIndex(i);
     888          90 :             IndexVector_t shifted{idx};
     889         270 :             for (index_t j = 0; j < desc.getNumberOfDimensions(); ++j) {
     890         180 :                 shifted[j] = ((idx[j] + midPoint[j]) + numOfCoeffsPerDim[j]) % numOfCoeffsPerDim[j];
     891         180 :             }
     892          90 :             copy(shifted) = dc(idx);
     893          90 :         }
     894           5 :         return copy;
     895           5 :     }
     896             : 
     897             :     template <typename data_t>
     898             :     DataContainer<data_t> fftShift2D(const DataContainer<data_t>& dc)
     899          24 :     {
     900          24 :         assert(dc.getDataDescriptor().getNumberOfDimensions() == 2
     901          24 :                && "DataContainer::fftShift2D: currently only supporting 2D signals");
     902             : 
     903          24 :         const DataDescriptor& dataDescriptor = dc.getDataDescriptor();
     904          24 :         IndexVector_t numOfCoeffsPerDim = dataDescriptor.getNumberOfCoefficientsPerDimension();
     905          24 :         index_t m = numOfCoeffsPerDim[0];
     906          24 :         index_t n = numOfCoeffsPerDim[1];
     907             : 
     908          24 :         index_t firstShift = m / 2;
     909          24 :         index_t secondShift = n / 2;
     910             : 
     911          24 :         DataContainer<data_t> copyDC(dataDescriptor);
     912             : 
     913          96 :         for (index_t i = 0; i < m; ++i) {
     914         352 :             for (index_t j = 0; j < n; ++j) {
     915         280 :                 copyDC((i + firstShift) % m, (j + secondShift) % n) = dc(i, j);
     916         280 :             }
     917          72 :         }
     918             : 
     919          24 :         return copyDC;
     920          24 :     }
     921             : 
     922             :     template <typename data_t>
     923             :     DataContainer<data_t> ifftShift2D(const DataContainer<data_t>& dc)
     924          24 :     {
     925          24 :         assert(dc.getDataDescriptor().getNumberOfDimensions() == 2
     926          24 :                && "DataContainer::ifftShift2D: currently only supporting 2D signals");
     927             : 
     928          24 :         const DataDescriptor& dataDescriptor = dc.getDataDescriptor();
     929          24 :         IndexVector_t numOfCoeffsPerDim = dataDescriptor.getNumberOfCoefficientsPerDimension();
     930          24 :         index_t m = numOfCoeffsPerDim[0];
     931          24 :         index_t n = numOfCoeffsPerDim[1];
     932             : 
     933          24 :         index_t firstShift = -m / 2;
     934          24 :         index_t secondShift = -n / 2;
     935             : 
     936          24 :         DataContainer<data_t> copyDC(dataDescriptor);
     937             : 
     938          96 :         for (index_t i = 0; i < m; ++i) {
     939         352 :             for (index_t j = 0; j < n; ++j) {
     940         280 :                 index_t leftIndex = (((i + firstShift) % m) + m) % m;
     941         280 :                 index_t rightIndex = (((j + secondShift) % n) + n) % n;
     942         280 :                 copyDC(leftIndex, rightIndex) = dc(i, j);
     943         280 :             }
     944          72 :         }
     945             : 
     946          24 :         return copyDC;
     947          24 :     }
     948             : 
     949             :     template <typename data_t>
     950             :     DataContainer<data_t> clip(const DataContainer<data_t>& dc, data_t min, data_t max)
     951         178 :     {
     952         178 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     953         178 :         elsa::clip(dc.begin(), dc.end(), copy.begin(), min, max);
     954         178 :         return copy;
     955         178 :     }
     956             : 
     957             :     template <typename data_t>
     958             :     DataContainer<data_t> exp(const DataContainer<data_t>& dc)
     959         102 :     {
     960         102 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     961         102 :         elsa::exp(dc.begin(), dc.end(), copy.begin());
     962         102 :         return copy;
     963         102 :     }
     964             : 
     965             :     template <typename data_t>
     966             :     DataContainer<data_t> log(const DataContainer<data_t>& dc)
     967          17 :     {
     968          17 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     969          17 :         elsa::log(dc.begin(), dc.end(), copy.begin());
     970          17 :         return copy;
     971          17 :     }
     972             : 
     973             :     template <typename data_t>
     974             :     DataContainer<data_t> square(const DataContainer<data_t>& dc)
     975         151 :     {
     976         151 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     977         151 :         elsa::square(dc.begin(), dc.end(), copy.begin());
     978         151 :         return copy;
     979         151 :     }
     980             : 
     981             :     template <typename data_t>
     982             :     DataContainer<data_t> sq(const DataContainer<data_t>& dc)
     983          54 :     {
     984          54 :         return square(dc);
     985          54 :     }
     986             : 
     987             :     template <typename data_t>
     988             :     DataContainer<data_t> sqrt(const DataContainer<data_t>& dc)
     989          43 :     {
     990          43 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     991          43 :         elsa::sqrt(dc.begin(), dc.end(), copy.begin());
     992          43 :         return copy;
     993          43 :     }
     994             : 
     995             :     template <typename data_t>
     996             :     DataContainer<data_t> minimum(const DataContainer<data_t>& dc, SelfType_t<data_t> scalar)
     997           7 :     {
     998           7 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     999           7 :         elsa::minimum(dc.begin(), dc.end(), scalar, copy.begin());
    1000           7 :         return copy;
    1001           7 :     }
    1002             : 
    1003             :     template <typename data_t>
    1004             :     DataContainer<data_t> maximum(const DataContainer<data_t>& dc, SelfType_t<data_t> scalar)
    1005         178 :     {
    1006         178 :         DataContainer<data_t> copy(dc.getDataDescriptor());
    1007         178 :         elsa::maximum(dc.begin(), dc.end(), scalar, copy.begin());
    1008         178 :         return copy;
    1009         178 :     }
    1010             : 
    1011             :     template <class data_t>
    1012             :     DataContainer<data_t> materialize(const DataContainer<data_t>& x)
    1013         710 :     {
    1014         710 :         if (x.isOwning()) {
    1015          82 :             return x;
    1016         628 :         } else {
    1017         628 :             ContiguousStorage<data_t> storage(x.begin(), x.end());
    1018         628 :             return DataContainer(x.getDataDescriptor(), storage);
    1019         628 :         }
    1020         710 :     }
    1021             : 
    1022             :     template <class data_t>
    1023             :     DataContainer<data_t> bessel_log_0(const DataContainer<data_t>& dc)
    1024           4 :     {
    1025           4 :         DataContainer<data_t> copy(dc.getDataDescriptor());
    1026           4 :         elsa::bessel_log_0(dc.begin(), dc.end(), copy.begin());
    1027           4 :         return copy;
    1028           4 :     }
    1029             : 
    1030             :     template <class data_t>
    1031             :     DataContainer<data_t> bessel_1_0(const DataContainer<data_t>& dc)
    1032           8 :     {
    1033           8 :         DataContainer<data_t> copy(dc.getDataDescriptor());
    1034           8 :         elsa::bessel_1_0(dc.begin(), dc.end(), copy.begin());
    1035           8 :         return copy;
    1036           8 :     }
    1037             : 
    1038             :     template <typename data_t>
    1039             :     DataContainer<value_type_of_t<data_t>> cwiseAbs(const DataContainer<data_t>& dc)
    1040         117 :     {
    1041         117 :         using T = GetFloatingPointType_t<data_t>;
    1042         117 :         DataContainer<T> copy(dc.getDataDescriptor());
    1043             : 
    1044         117 :         elsa::cwiseAbs(dc.begin(), dc.end(), copy.begin());
    1045         117 :         return copy;
    1046         117 :     }
    1047             : 
    1048             :     template <typename data_t>
    1049             :     DataContainer<value_type_of_t<data_t>> sign(const DataContainer<data_t>& dc)
    1050         109 :     {
    1051         109 :         using T = GetFloatingPointType_t<data_t>;
    1052         109 :         DataContainer<T> copy(dc.getDataDescriptor());
    1053             : 
    1054         109 :         elsa::sign(dc.begin(), dc.end(), copy.begin());
    1055         109 :         return copy;
    1056         109 :     }
    1057             : 
    1058             :     template <typename data_t>
    1059             :     DataContainer<value_type_of_t<data_t>> real(const DataContainer<data_t>& dc)
    1060         207 :     {
    1061         207 :         DataContainer<value_type_of_t<data_t>> result(dc.getDataDescriptor());
    1062         207 :         elsa::real(dc.begin(), dc.end(), result.begin());
    1063         207 :         return result;
    1064         207 :     }
    1065             : 
    1066             :     template <typename data_t>
    1067             :     DataContainer<value_type_of_t<data_t>> imag(const DataContainer<data_t>& dc)
    1068           5 :     {
    1069           5 :         DataContainer<value_type_of_t<data_t>> result(dc.getDataDescriptor());
    1070           5 :         elsa::imag(dc.begin(), dc.end(), result.begin());
    1071           5 :         return result;
    1072           5 :     }
    1073             : 
    1074             :     template <typename data_t>
    1075             :     DataContainer<add_complex_t<data_t>> asComplex(const DataContainer<data_t>& dc)
    1076         572 :     {
    1077         572 :         if constexpr (isComplex<data_t>) {
    1078         448 :             return dc;
    1079         448 :         } else {
    1080         448 :             DataContainer<complex<data_t>> ret{dc.getDataDescriptor()};
    1081             : 
    1082             :             // extend with complex zero value
    1083         448 :             elsa::cast(dc.begin(), dc.end(), ret.begin());
    1084         448 :             return ret;
    1085         448 :         }
    1086         572 :     }
    1087             : 
    1088             :     template <typename data_t>
    1089             :     DataContainer<data_t> operator-(const DataContainer<data_t>& lhs,
    1090             :                                     const DataContainer<data_t>& rhs)
    1091        1982 :     {
    1092             :         // TODO: Do size checking!
    1093        1982 :         DataContainer<data_t> ret{lhs.getDataDescriptor()};
    1094        1982 :         elsa::sub(lhs.begin(), lhs.end(), rhs.begin(), ret.begin());
    1095        1982 :         return ret;
    1096        1982 :     }
    1097             : 
    1098             :     template <typename data_t, typename Scalar, typename>
    1099             :     DataContainer<std::common_type_t<data_t, Scalar>> operator-(const DataContainer<data_t>& dc,
    1100             :                                                                 const Scalar& s)
    1101         111 :     {
    1102         111 :         using T = std::common_type_t<data_t, Scalar>;
    1103             : 
    1104         111 :         DataContainer<T> ret{dc.getDataDescriptor()};
    1105         111 :         elsa::subScalar(dc.begin(), dc.end(), T(s), ret.begin());
    1106         111 :         return ret;
    1107         111 :     }
    1108             : 
    1109             :     template <typename Scalar, typename data_t, typename>
    1110             :     DataContainer<std::common_type_t<Scalar, data_t>> operator-(const Scalar& s,
    1111             :                                                                 const DataContainer<data_t>& dc)
    1112          29 :     {
    1113          29 :         using T = std::common_type_t<Scalar, data_t>;
    1114             : 
    1115          29 :         DataContainer<T> ret{dc.getDataDescriptor()};
    1116          29 :         elsa::subScalar(T(s), dc.begin(), dc.end(), ret.begin());
    1117          29 :         return ret;
    1118          29 :     }
    1119             : 
    1120             :     template <typename data_t>
    1121             :     DataContainer<data_t> operator/(const DataContainer<data_t>& lhs,
    1122             :                                     const DataContainer<data_t>& rhs)
    1123          67 :     {
    1124          67 :         DataContainer<data_t> ret{lhs.getDataDescriptor()};
    1125          67 :         elsa::div(lhs.begin(), lhs.end(), rhs.begin(), ret.begin());
    1126          67 :         return ret;
    1127          67 :     }
    1128             : 
    1129             :     template <typename data_t, typename Scalar, typename>
    1130             :     DataContainer<std::common_type_t<data_t, Scalar>> operator/(const DataContainer<data_t>& dc,
    1131             :                                                                 const Scalar& s)
    1132          45 :     {
    1133          45 :         using T = std::common_type_t<data_t, Scalar>;
    1134             : 
    1135          45 :         DataContainer<T> ret{dc.getDataDescriptor()};
    1136          45 :         elsa::divScalar(dc.begin(), dc.end(), T(s), ret.begin());
    1137          45 :         return ret;
    1138          45 :     }
    1139             : 
    1140             :     template <typename Scalar, typename data_t, typename>
    1141             :     DataContainer<std::common_type_t<Scalar, data_t>> operator/(const Scalar& s,
    1142             :                                                                 const DataContainer<data_t>& dc)
    1143          39 :     {
    1144          39 :         using T = std::common_type_t<Scalar, data_t>;
    1145             : 
    1146          39 :         DataContainer<T> ret{dc.getDataDescriptor()};
    1147          39 :         elsa::divScalar(T(s), dc.begin(), dc.end(), ret.begin());
    1148          39 :         return ret;
    1149          39 :     }
    1150             : 
    1151             :     template <typename xdata_t, typename ydata_t>
    1152             :     DataContainer<value_type_of_t<std::common_type_t<xdata_t, ydata_t>>>
    1153             :         cwiseMax(const DataContainer<xdata_t>& lhs, const DataContainer<ydata_t>& rhs)
    1154          20 :     {
    1155          20 :         using data_t = value_type_of_t<std::common_type_t<xdata_t, ydata_t>>;
    1156             : 
    1157          20 :         DataContainer<data_t> copy(rhs.getDataDescriptor());
    1158          20 :         elsa::cwiseMax(lhs.begin(), lhs.end(), rhs.begin(), copy.begin());
    1159          20 :         return copy;
    1160          20 :     }
    1161             : 
    1162             :     template <typename xdata_t, typename ydata_t>
    1163             :     DataContainer<value_type_of_t<std::common_type_t<xdata_t, ydata_t>>>
    1164             :         cwiseMin(const DataContainer<xdata_t>& lhs, const DataContainer<ydata_t>& rhs)
    1165           0 :     {
    1166           0 :         using data_t = value_type_of_t<std::common_type_t<xdata_t, ydata_t>>;
    1167             : 
    1168           0 :         DataContainer<data_t> copy(rhs.getDataDescriptor());
    1169           0 :         elsa::cwiseMin(lhs.begin(), lhs.end(), rhs.begin(), copy.begin());
    1170           0 :         return copy;
    1171           0 :     }
    1172             : 
    1173             :     template <class data_t>
    1174             :     DataContainer<data_t> lincomb(SelfType_t<data_t> a, const DataContainer<data_t>& x,
    1175             :                                   SelfType_t<data_t> b, const DataContainer<data_t>& y)
    1176          20 :     {
    1177          20 :         if (x.getDataDescriptor() != y.getDataDescriptor()) {
    1178           8 :             throw InvalidArgumentError("lincomb: x and y are of different size");
    1179           8 :         }
    1180             : 
    1181          12 :         auto out = DataContainer<data_t>(x.getDataDescriptor());
    1182          12 :         lincomb(a, x, b, y, out);
    1183          12 :         return out;
    1184          12 :     }
    1185             : 
    1186             :     template <class data_t>
    1187             :     void lincomb(SelfType_t<data_t> a, const DataContainer<data_t>& x, SelfType_t<data_t> b,
    1188             :                  const DataContainer<data_t>& y, DataContainer<data_t>& out)
    1189        5182 :     {
    1190        5182 :         if (x.getDataDescriptor() != y.getDataDescriptor()) {
    1191           0 :             throw InvalidArgumentError("lincomb: x and y are of different size");
    1192           0 :         }
    1193             : 
    1194        5182 :         if (x.getDataDescriptor() != out.getDataDescriptor()) {
    1195           4 :             throw InvalidArgumentError("lincomb: input and output vectors are of different size");
    1196           4 :         }
    1197             : 
    1198        5178 :         lincomb(a, x.begin(), x.end(), b, y.begin(), out.begin());
    1199        5178 :     }
    1200             : 
    1201             :     template <class data_t>
    1202             :     DataContainer<data_t> zeros(const DataDescriptor& desc)
    1203          74 :     {
    1204          74 :         return full<data_t>(desc, 0);
    1205          74 :     }
    1206             : 
    1207             :     template <class data_t>
    1208             :     DataContainer<data_t> zeroslike(const DataContainer<data_t>& dc)
    1209          30 :     {
    1210          30 :         return zeros<data_t>(dc.getDataDescriptor());
    1211          30 :     }
    1212             : 
    1213             :     template <class data_t>
    1214             :     DataContainer<data_t> ones(const DataDescriptor& desc)
    1215          16 :     {
    1216          16 :         return full<data_t>(desc, 1);
    1217          16 :     }
    1218             : 
    1219             :     template <class data_t>
    1220             :     DataContainer<data_t> oneslike(const DataContainer<data_t>& dc)
    1221           4 :     {
    1222           4 :         return ones<data_t>(dc.getDataDescriptor());
    1223           4 :     }
    1224             : 
    1225             :     template <class data_t>
    1226             :     DataContainer<data_t> full(const DataDescriptor& desc, SelfType_t<data_t> value)
    1227         198 :     {
    1228         198 :         auto dc = DataContainer<data_t>(desc);
    1229         198 :         dc = value;
    1230         198 :         return dc;
    1231         198 :     }
    1232             : 
    1233             :     template <class data_t>
    1234             :     DataContainer<data_t> fulllike(const DataContainer<data_t>& dc, SelfType_t<data_t> value)
    1235           4 :     {
    1236           4 :         return full<data_t>(dc.getDataDescriptor(), value);
    1237           4 :     }
    1238             : 
    1239             :     template <class data_t>
    1240             :     DataContainer<data_t> empty(const DataDescriptor& desc)
    1241         883 :     {
    1242         883 :         return DataContainer<data_t>(desc);
    1243         883 :     }
    1244             : 
    1245             :     template <class data_t>
    1246             :     DataContainer<data_t> emptylike(const DataContainer<data_t>& dc)
    1247         526 :     {
    1248         526 :         return empty<data_t>(dc.getDataDescriptor());
    1249         526 :     }
    1250             : 
    1251             :     // ------------------------------------------
    1252             :     // explicit template instantiation
    1253             :     template class DataContainer<float>;
    1254             :     template class DataContainer<complex<float>>;
    1255             :     template class DataContainer<double>;
    1256             :     template class DataContainer<complex<double>>;
    1257             :     template class DataContainer<index_t>;
    1258             : 
    1259             :     template DataContainer<float> clip<float>(const DataContainer<float>& dc, float min, float max);
    1260             :     template DataContainer<double> clip<double>(const DataContainer<double>& dc, double min,
    1261             :                                                 double max);
    1262             : 
    1263             :     template DataContainer<float> concatenate<float>(const DataContainer<float>&,
    1264             :                                                      const DataContainer<float>&);
    1265             :     template DataContainer<double> concatenate<double>(const DataContainer<double>&,
    1266             :                                                        const DataContainer<double>&);
    1267             :     template DataContainer<complex<float>>
    1268             :         concatenate<complex<float>>(const DataContainer<complex<float>>&,
    1269             :                                     const DataContainer<complex<float>>&);
    1270             :     template DataContainer<complex<double>>
    1271             :         concatenate<complex<double>>(const DataContainer<complex<double>>&,
    1272             :                                      const DataContainer<complex<double>>&);
    1273             : 
    1274             :     template void lincomb<float>(SelfType_t<float>, const DataContainer<float>&, SelfType_t<float>,
    1275             :                                  const DataContainer<float>&, DataContainer<float>&);
    1276             :     template void lincomb<double>(SelfType_t<double>, const DataContainer<double>&,
    1277             :                                   SelfType_t<double>, const DataContainer<double>&,
    1278             :                                   DataContainer<double>&);
    1279             :     template DataContainer<float> lincomb<float>(SelfType_t<float>, const DataContainer<float>&,
    1280             :                                                  SelfType_t<float>, const DataContainer<float>&);
    1281             :     template DataContainer<double> lincomb<double>(SelfType_t<double>, const DataContainer<double>&,
    1282             :                                                    SelfType_t<double>,
    1283             :                                                    const DataContainer<double>&);
    1284             : 
    1285             : #define ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET(fn, type) \
    1286             :     template DataContainer<value_type_of_t<type>> fn<type>(const DataContainer<type>&);
    1287             : 
    1288             : #define ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET_TYPES(fn)        \
    1289             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET(fn, index_t);        \
    1290             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET(fn, float);          \
    1291             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET(fn, double);         \
    1292             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET(fn, complex<float>); \
    1293             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET(fn, complex<double>);
    1294             : 
    1295             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET_TYPES(cwiseAbs)
    1296             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET_TYPES(real)
    1297             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET_TYPES(imag)
    1298             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET_TYPES(sign)
    1299             : 
    1300             : #undef ELSA_INSTANTIATE_UNARY_TRANSFORMATION_REAL_RET
    1301             : 
    1302             : #define ELSA_INSTANTIATE_UNARY_TRANSFORMATION(fn, type) \
    1303             :     template DataContainer<type> fn<type>(const DataContainer<type>&);
    1304             : 
    1305             : #define ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(fn)       \
    1306             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(fn, index_t)        \
    1307             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(fn, float)          \
    1308             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(fn, double)         \
    1309             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(fn, complex<float>) \
    1310             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(fn, complex<double>)
    1311             : 
    1312             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(fftShift)
    1313             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(ifftShift)
    1314             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(fftShift2D)
    1315             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(ifftShift2D)
    1316             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(exp)
    1317             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(log)
    1318             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(square)
    1319             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(sq)
    1320             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(sqrt)
    1321             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_TYPES(materialize)
    1322             : 
    1323             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(bessel_log_0, float)
    1324             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(bessel_1_0, float)
    1325             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(bessel_log_0, double)
    1326             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION(bessel_1_0, double)
    1327             : 
    1328             : #undef ELSA_INSTANTIATE_UNARY_TRANSFORMATION
    1329             : 
    1330             : #define ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(fn, type) \
    1331             :     template DataContainer<type> fn<type>(const DataContainer<type>&, SelfType_t<type>);
    1332             : 
    1333             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(minimum, index_t)
    1334             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(minimum, float)
    1335             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(minimum, double)
    1336             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(minimum, complex<float>)
    1337             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(minimum, complex<double>)
    1338             : 
    1339             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(maximum, index_t)
    1340             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(maximum, float)
    1341             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(maximum, double)
    1342             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(maximum, complex<float>)
    1343             :     ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX(maximum, complex<double>)
    1344             : 
    1345             : #undef ELSA_INSTANTIATE_UNARY_TRANSFORMATION_MINMAX
    1346             : 
    1347             : #define ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(fn, type)                             \
    1348             :     template DataContainer<value_type_of_t<std::common_type_t<type, type>>> fn<type, type>( \
    1349             :         const DataContainer<type>&, const DataContainer<type>&);
    1350             : 
    1351             : #define ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(fn, type1, type2)                          \
    1352             :     template DataContainer<value_type_of_t<std::common_type_t<type1, type2>>> fn<type1, type2>( \
    1353             :         const DataContainer<type1>&, const DataContainer<type2>&);                              \
    1354             :     template DataContainer<value_type_of_t<std::common_type_t<type2, type1>>> fn<type2, type1>( \
    1355             :         const DataContainer<type2>&, const DataContainer<type1>&);
    1356             : 
    1357             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMax, index_t)
    1358             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMax, float)
    1359             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMax, double)
    1360             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMax, thrust::complex<float>)
    1361             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMax, thrust::complex<double>)
    1362             : 
    1363             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, index_t, float)
    1364             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, index_t, double)
    1365             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, index_t, thrust::complex<float>)
    1366             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, index_t, thrust::complex<double>)
    1367             : 
    1368             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, float, double)
    1369             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, float, thrust::complex<float>)
    1370             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, float, thrust::complex<double>)
    1371             : 
    1372             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, double, thrust::complex<float>)
    1373             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, double, thrust::complex<double>)
    1374             : 
    1375             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMax, thrust::complex<float>,
    1376             :                                                  thrust::complex<double>)
    1377             : 
    1378             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMin, index_t)
    1379             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMin, float)
    1380             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMin, double)
    1381             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMin, thrust::complex<float>)
    1382             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_SINGLE(cwiseMin, thrust::complex<double>)
    1383             : 
    1384             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, index_t, float)
    1385             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, index_t, double)
    1386             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, index_t, thrust::complex<float>)
    1387             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, index_t, thrust::complex<double>)
    1388             : 
    1389             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, float, double)
    1390             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, float, thrust::complex<float>)
    1391             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, float, thrust::complex<double>)
    1392             : 
    1393             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, double, thrust::complex<float>)
    1394             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, double, thrust::complex<double>)
    1395             : 
    1396             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED(cwiseMin, thrust::complex<float>,
    1397             :                                                  thrust::complex<double>)
    1398             : 
    1399             : #undef ELSA_INSTANTIATE_BINARY_TRANSFORMATION_MIXED
    1400             : 
    1401             : #define ELSA_INSTANTIATE_BINARY_TRANSFORMATION(fn, type) \
    1402             :     template DataContainer<type> fn<type>(const DataContainer<type>&, const DataContainer<type>&);
    1403             : 
    1404             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator-, index_t)
    1405             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator-, float)
    1406             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator-, double)
    1407             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator-, thrust::complex<float>)
    1408             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator-, thrust::complex<double>)
    1409             : 
    1410             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator/, index_t)
    1411             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator/, float)
    1412             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator/, double)
    1413             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator/, thrust::complex<float>)
    1414             :     ELSA_INSTANTIATE_BINARY_TRANSFORMATION(operator/, thrust::complex<double>)
    1415             : 
    1416             : #undef ELSA_INSTANTIATE_BINARY_TRANSFORMATION
    1417             : 
    1418             : #define ELSA_INSTANTIATE_OPERATOR_MIXED(fn, dtype, stype)                            \
    1419             :     template DataContainer<std::common_type_t<dtype, stype>> fn<dtype, stype, void>( \
    1420             :         const DataContainer<dtype>&, const stype&);                                  \
    1421             :     template DataContainer<std::common_type_t<stype, dtype>> fn<stype, dtype, void>( \
    1422             :         const stype&, const DataContainer<dtype>&);
    1423             : 
    1424             : #define ELSA_INSTANTIATE_OPERATOR_MIXED_ALL(fn)                                          \
    1425             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, index_t, int)                                    \
    1426             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, index_t, index_t)                                \
    1427             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, index_t, float)                                  \
    1428             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, index_t, double)                                 \
    1429             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, index_t, thrust::complex<float>)                 \
    1430             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, index_t, thrust::complex<double>)                \
    1431             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, float, int)                                      \
    1432             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, float, index_t)                                  \
    1433             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, float, float)                                    \
    1434             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, float, double)                                   \
    1435             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, float, thrust::complex<float>)                   \
    1436             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, float, thrust::complex<double>)                  \
    1437             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, double, int)                                     \
    1438             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, double, index_t)                                 \
    1439             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, double, float)                                   \
    1440             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, double, double)                                  \
    1441             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, double, thrust::complex<float>)                  \
    1442             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, double, thrust::complex<double>)                 \
    1443             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<float>, int)                     \
    1444             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<float>, index_t)                 \
    1445             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<float>, float)                   \
    1446             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<float>, double)                  \
    1447             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<float>, thrust::complex<float>)  \
    1448             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<float>, thrust::complex<double>) \
    1449             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<double>, int)                    \
    1450             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<double>, index_t)                \
    1451             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<double>, float)                  \
    1452             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<double>, double)                 \
    1453             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<double>, thrust::complex<float>) \
    1454             :     ELSA_INSTANTIATE_OPERATOR_MIXED(fn, thrust::complex<double>, thrust::complex<double>)
    1455             : 
    1456             :     ELSA_INSTANTIATE_OPERATOR_MIXED_ALL(operator/)
    1457             :     ELSA_INSTANTIATE_OPERATOR_MIXED_ALL(operator-)
    1458             : 
    1459             : #define ELSA_INSTANTIATE_AS_COMPLEX(type) \
    1460             :     template DataContainer<add_complex_t<type>> asComplex<type>(const DataContainer<type>&);
    1461             : 
    1462             :     ELSA_INSTANTIATE_AS_COMPLEX(index_t)
    1463             :     ELSA_INSTANTIATE_AS_COMPLEX(float)
    1464             :     ELSA_INSTANTIATE_AS_COMPLEX(double)
    1465             :     ELSA_INSTANTIATE_AS_COMPLEX(complex<float>)
    1466             :     ELSA_INSTANTIATE_AS_COMPLEX(complex<double>)
    1467             : 
    1468             : #undef ELSA_INSTANTIATE_AS_COMPLEX
    1469             : 
    1470             : #define ELSA_INSTANTIATE_FILL_FN(fn, type) \
    1471             :     template DataContainer<type> fn<type>(const DataDescriptor&);
    1472             : 
    1473             : #define ELSA_INSTANTIATE_FILL_FN_TYPES(fn)       \
    1474             :     ELSA_INSTANTIATE_FILL_FN(fn, index_t)        \
    1475             :     ELSA_INSTANTIATE_FILL_FN(fn, float)          \
    1476             :     ELSA_INSTANTIATE_FILL_FN(fn, double)         \
    1477             :     ELSA_INSTANTIATE_FILL_FN(fn, complex<float>) \
    1478             :     ELSA_INSTANTIATE_FILL_FN(fn, complex<double>)
    1479             : 
    1480             :     ELSA_INSTANTIATE_FILL_FN_TYPES(empty)
    1481             :     ELSA_INSTANTIATE_FILL_FN_TYPES(zeros)
    1482             :     ELSA_INSTANTIATE_FILL_FN_TYPES(ones)
    1483             : 
    1484             : #undef ELSA_INSTANTIATE_FILL_FN
    1485             : #undef ELSA_INSTANTIATE_FILL_FN_TYPES
    1486             : 
    1487             : #define ELSA_INSTANTIATE_FILL_LIKE_FN(fn, type) \
    1488             :     template DataContainer<type> fn<type>(const DataContainer<type>&);
    1489             : 
    1490             : #define ELSA_INSTANTIATE_FILL_LIKE_FN_TYPES(fn)       \
    1491             :     ELSA_INSTANTIATE_FILL_LIKE_FN(fn, index_t)        \
    1492             :     ELSA_INSTANTIATE_FILL_LIKE_FN(fn, float)          \
    1493             :     ELSA_INSTANTIATE_FILL_LIKE_FN(fn, double)         \
    1494             :     ELSA_INSTANTIATE_FILL_LIKE_FN(fn, complex<float>) \
    1495             :     ELSA_INSTANTIATE_FILL_LIKE_FN(fn, complex<double>)
    1496             : 
    1497             :     ELSA_INSTANTIATE_FILL_LIKE_FN_TYPES(emptylike)
    1498             :     ELSA_INSTANTIATE_FILL_LIKE_FN_TYPES(zeroslike)
    1499             :     ELSA_INSTANTIATE_FILL_LIKE_FN_TYPES(oneslike)
    1500             : 
    1501             : #undef ELSA_INSTANTIATE_FILL_LIKE_FN
    1502             : #undef ELSA_INSTANTIATE_FILL_LIKE_FN_TYPES
    1503             : 
    1504             : #define ELSA_INSTANTIATE_FULL_FN(type)                                                \
    1505             :     template DataContainer<type> full<type>(const DataDescriptor&, SelfType_t<type>); \
    1506             :     template DataContainer<type> fulllike<type>(const DataContainer<type>&, SelfType_t<type>);
    1507             : 
    1508             : #define ELSA_INSTANTIATE_FULL_FN_TYPES()     \
    1509             :     ELSA_INSTANTIATE_FULL_FN(index_t)        \
    1510             :     ELSA_INSTANTIATE_FULL_FN(float)          \
    1511             :     ELSA_INSTANTIATE_FULL_FN(double)         \
    1512             :     ELSA_INSTANTIATE_FULL_FN(complex<float>) \
    1513             :     ELSA_INSTANTIATE_FULL_FN(complex<double>)
    1514             : 
    1515             :     ELSA_INSTANTIATE_FULL_FN_TYPES()
    1516             : 
    1517             : #undef ELSA_INSTANTIATE_FULL_FN
    1518             : #undef ELSA_INSTANTIATE_FULL_FN_TYPES
    1519             : #define ELSA_INSTANTIATE_RFFT_IRFFT(type)                                                    \
    1520             :     template DataContainer<complex<type>> rfft(const DataContainer<type>& dc, FFTNorm norm,  \
    1521             :                                                FFTPolicy policy);                            \
    1522             :     template DataContainer<type> irfft(const DataContainer<complex<type>>& dc, FFTNorm norm, \
    1523             :                                        FFTPolicy policy);
    1524             : 
    1525             :     ELSA_INSTANTIATE_RFFT_IRFFT(float)
    1526             :     ELSA_INSTANTIATE_RFFT_IRFFT(double)
    1527             : 
    1528             : #undef ELSA_INSTANTIATE_RFFT_IRFFT
    1529             : } // namespace elsa

Generated by: LCOV version 1.14