From 691002e2544cf9b520c6cde0df7622e0fd5eeea8 2017-08-07 08:07:25 From: Alexandre Leroux Date: 2017-08-07 08:07:25 Subject: [PATCH] Merge branch 'feature/MultidimArrayData' into develop --- diff --git a/core/include/Common/SortUtils.h b/core/include/Common/SortUtils.h index 42a5c76..b79705c 100644 --- a/core/include/Common/SortUtils.h +++ b/core/include/Common/SortUtils.h @@ -34,6 +34,31 @@ struct SortUtils { [&](int i, int j) { return compare(container.at(i), container.at(j)); }); return permutation; } + + /** + * Sorts a container according to indices passed in parameter + * @param container the container sorted + * @param sortPermutation the indices used to sort the container + * @return the container sorted + * @warning no verification is made on validity of sortPermutation (i.e. the vector has unique + * indices and its range is [0 ; vector.size()[ ) + */ + template + static Container sort(const Container &container, const std::vector &sortPermutation) + { + if (container.size() != sortPermutation.size()) { + return Container{}; + } + + // Inits result + auto sortedData = Container{}; + sortedData.resize(container.size()); + + std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(), + [&container](int i) { return container.at(i); }); + + return sortedData; + } }; #endif // SCIQLOP_SORTUTILS_H diff --git a/core/include/Data/ArrayData.h b/core/include/Data/ArrayData.h index edab83a..ccca546 100644 --- a/core/include/Data/ArrayData.h +++ b/core/include/Data/ArrayData.h @@ -1,17 +1,56 @@ #ifndef SCIQLOP_ARRAYDATA_H #define SCIQLOP_ARRAYDATA_H +#include + #include #include #include #include +template +class ArrayData; + +using DataContainer = QVector >; + +namespace arraydata_detail { + +/// Struct used to sort ArrayData +template +struct Sort { + static std::shared_ptr > sort(const DataContainer &data, + const std::vector &sortPermutation) + { + auto nbComponents = data.size(); + auto sortedData = DataContainer(nbComponents); + + for (auto i = 0; i < nbComponents; ++i) { + sortedData[i] = SortUtils::sort(data.at(i), sortPermutation); + } + + return std::make_shared >(std::move(sortedData)); + } +}; + +/// Specialization for uni-dimensional ArrayData +template <> +struct Sort<1> { + static std::shared_ptr > sort(const DataContainer &data, + const std::vector &sortPermutation) + { + return std::make_shared >(SortUtils::sort(data.at(0), sortPermutation)); + } +}; + +} // namespace arraydata_detail + /** * @brief The ArrayData class represents a dataset for a data series. * * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim - * template-parameter. + * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same + * number of values * * @tparam Dim the dimension of the ArrayData (one or two) * @sa IDataSeries @@ -19,16 +58,9 @@ template class ArrayData { public: - /** - * Ctor for a unidimensional ArrayData - * @param nbColumns the number of values the ArrayData will hold - */ - template > - explicit ArrayData(int nbColumns) : m_Data{1, QVector{}} - { - QWriteLocker locker{&m_Lock}; - m_Data[0].resize(nbColumns); - } + // ///// // + // Ctors // + // ///// // /** * Ctor for a unidimensional ArrayData @@ -37,86 +69,72 @@ public: template > explicit ArrayData(QVector data) : m_Data{1, QVector{}} { - QWriteLocker locker{&m_Lock}; m_Data[0] = std::move(data); } - /// Copy ctor - explicit ArrayData(const ArrayData &other) - { - QReadLocker otherLocker{&other.m_Lock}; - QWriteLocker locker{&m_Lock}; - m_Data = other.m_Data; - } - /** - * @return the data at a specified index - * @remarks index must be a valid position - * @remarks this method is only available for a unidimensional ArrayData + * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be + * greater than 2 and each component must have the same number of values + * @param data the data the ArrayData will hold + * @throws std::invalid_argument if the number of components is less than 2 + * @remarks if the number of values is not the same for each component, no value is set */ - template > - double at(int index) const noexcept + template > + explicit ArrayData(DataContainer data) { - QReadLocker locker{&m_Lock}; - return m_Data[0].at(index); - } + auto nbComponents = data.size(); + if (nbComponents < 2) { + throw std::invalid_argument{ + QString{"A multidimensional ArrayData must have at least 2 components (found: %1"} + .arg(data.size()) + .toStdString()}; + } - /** - * Sets a data at a specified index. The index has to be valid to be effective - * @param index the index to which the data will be set - * @param data the data to set - * @remarks this method is only available for a unidimensional ArrayData - */ - template > - void setData(int index, double data) noexcept - { - QWriteLocker locker{&m_Lock}; - if (index >= 0 && index < m_Data.at(0).size()) { - m_Data[0].replace(index, data); + auto nbValues = data.front().size(); + if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) { + return component.size() == nbValues; + })) { + m_Data = std::move(data); + } + else { + m_Data = DataContainer{nbComponents, QVector{}}; } } - /** - * @return the data as a vector - * @remarks this method is only available for a unidimensional ArrayData - */ - template > - QVector data() const noexcept + /// Copy ctor + explicit ArrayData(const ArrayData &other) { - QReadLocker locker{&m_Lock}; - return m_Data[0]; + QReadLocker otherLocker{&other.m_Lock}; + m_Data = other.m_Data; } - /** - * @return the data as a vector, as a const reference - * @remarks this method is only available for a unidimensional ArrayData - */ - template > - const QVector &cdata() const noexcept - { - QReadLocker locker{&m_Lock}; - return m_Data.at(0); - } + // /////////////// // + // General methods // + // /////////////// // /** - * Merges into the array data an other array data + * Merges into the array data an other array data. The two array datas must have the same number + * of components so the merge can be done * @param other the array data to merge with * @param prepend if true, the other array data is inserted at the beginning, otherwise it is * inserted at the end - * @remarks this method is only available for a unidimensional ArrayData */ - template > - void add(const ArrayData<1> &other, bool prepend = false) + void add(const ArrayData &other, bool prepend = false) { QWriteLocker locker{&m_Lock}; - if (!m_Data.empty()) { - QReadLocker otherLocker{&other.m_Lock}; + QReadLocker otherLocker{&other.m_Lock}; + + auto nbComponents = m_Data.size(); + if (nbComponents != other.m_Data.size()) { + return; + } + for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) { if (prepend) { - const auto &otherData = other.data(); + const auto &otherData = other.data(componentIndex); const auto otherDataSize = otherData.size(); - auto &data = m_Data[0]; + auto &data = m_Data[componentIndex]; data.insert(data.begin(), otherDataSize, 0.); for (auto i = 0; i < otherDataSize; ++i) { @@ -124,45 +142,88 @@ public: } } else { - m_Data[0] += other.data(); + m_Data[componentIndex] += other.data(componentIndex); } } } - template > + void clear() + { + QWriteLocker locker{&m_Lock}; + + auto nbComponents = m_Data.size(); + for (auto i = 0; i < nbComponents; ++i) { + m_Data[i].clear(); + } + } + + /** + * @return the data of a component + * @param componentIndex the index of the component to retrieve the data + * @return the component's data, empty vector if the index is invalid + */ + QVector data(int componentIndex) const noexcept + { + QReadLocker locker{&m_Lock}; + + return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex) + : QVector{}; + } + + /// @return the size (i.e. number of values) of a single component + /// @remarks in a case of a two-dimensional ArrayData, each component has the same size int size() const { QReadLocker locker{&m_Lock}; return m_Data[0].size(); } - template > - std::shared_ptr > sort(const std::vector sortPermutation) + std::shared_ptr > sort(const std::vector &sortPermutation) { QReadLocker locker{&m_Lock}; + return arraydata_detail::Sort::sort(m_Data, sortPermutation); + } - const auto &data = m_Data.at(0); - - // Inits result - auto sortedData = QVector{}; - sortedData.resize(data.size()); - - std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(), - [&data](int i) { return data[i]; }); + // ///////////// // + // 1-dim methods // + // ///////////// // - return std::make_shared >(std::move(sortedData)); + /** + * @return the data at a specified index + * @remarks index must be a valid position + * @remarks this method is only available for a unidimensional ArrayData + */ + template > + double at(int index) const noexcept + { + QReadLocker locker{&m_Lock}; + return m_Data[0].at(index); } + /** + * @return the data as a vector, as a const reference + * @remarks this method is only available for a unidimensional ArrayData + */ template > - void clear() + const QVector &cdata() const noexcept { - QWriteLocker locker{&m_Lock}; - m_Data[0].clear(); + QReadLocker locker{&m_Lock}; + return m_Data.at(0); } + /** + * @return the data as a vector + * @remarks this method is only available for a unidimensional ArrayData + */ + template > + QVector data() const noexcept + { + QReadLocker locker{&m_Lock}; + return m_Data[0]; + } private: - QVector > m_Data; + DataContainer m_Data; mutable QReadWriteLock m_Lock; }; diff --git a/core/vera-exclusions/exclusions.txt b/core/vera-exclusions/exclusions.txt index 8577c89..a4b02d1 100644 --- a/core/vera-exclusions/exclusions.txt +++ b/core/vera-exclusions/exclusions.txt @@ -6,6 +6,7 @@ DataSourceItem\.h:\d+:.*IPSIS_S01.* # Ignore false positive relative to a template class ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D) +ArrayData\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (arraydata_detail) ArrayData\.h:\d+:.*IPSIS_S06.*found: (D) ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim) DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.*