##// END OF EJS Templates
Merge pull request 241 from SCIQLOP-Initialisation develop...
perrinel -
r649:8ba2e73bb18a merge
parent child
Show More
@@ -0,0 +1,56
1 #ifndef SCIQLOP_ARRAYDATAITERATOR_H
2 #define SCIQLOP_ARRAYDATAITERATOR_H
3
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
6
7 #include <memory>
8
9 /**
10 * @brief The ArrayDataIteratorValue class represents the current value of an array data iterator.
11 * It offers standard access methods for the data in the series (at(), first()), but it is up to
12 * each array data to define its own implementation of how to retrieve this data (one-dim or two-dim
13 * array), by implementing the ArrayDataIteratorValue::Impl interface
14 * @sa ArrayDataIterator
15 */
16 class SCIQLOP_CORE_EXPORT ArrayDataIteratorValue {
17 public:
18 struct Impl {
19 virtual ~Impl() noexcept = default;
20 virtual std::unique_ptr<Impl> clone() const = 0;
21 virtual bool equals(const Impl &other) const = 0;
22 virtual void next() = 0;
23 virtual void prev() = 0;
24 virtual double at(int componentIndex) const = 0;
25 virtual double first() const = 0;
26 virtual double min() const = 0;
27 virtual double max() const = 0;
28 };
29
30 explicit ArrayDataIteratorValue(std::unique_ptr<Impl> impl);
31 ArrayDataIteratorValue(const ArrayDataIteratorValue &other);
32 ArrayDataIteratorValue(ArrayDataIteratorValue &&other) = default;
33 ArrayDataIteratorValue &operator=(ArrayDataIteratorValue other);
34
35 bool equals(const ArrayDataIteratorValue &other) const;
36
37 /// Advances to the next value
38 void next();
39 /// Moves back to the previous value
40 void prev();
41 /// Gets value of a specified component
42 double at(int componentIndex) const;
43 /// Gets value of first component
44 double first() const;
45 /// Gets min value among all components
46 double min() const;
47 /// Gets max value among all components
48 double max() const;
49
50 private:
51 std::unique_ptr<Impl> m_Impl;
52 };
53
54 using ArrayDataIterator = SqpIterator<ArrayDataIteratorValue>;
55
56 #endif // SCIQLOP_ARRAYDATAITERATOR_H
@@ -0,0 +1,54
1 #ifndef SCIQLOP_SQPITERATOR_H
2 #define SCIQLOP_SQPITERATOR_H
3
4 #include "CoreGlobal.h"
5
6 /**
7 * @brief The SqpIterator class represents an iterator used in SciQlop. It defines all operators
8 * needed for a standard forward iterator
9 * @tparam T the type of object handled in iterator
10 * @sa http://www.cplusplus.com/reference/iterator/
11 */
12 template <typename T>
13 class SCIQLOP_CORE_EXPORT SqpIterator {
14 public:
15 using iterator_category = std::forward_iterator_tag;
16 using value_type = const T;
17 using difference_type = std::ptrdiff_t;
18 using pointer = value_type *;
19 using reference = value_type &;
20
21 explicit SqpIterator(T value) : m_CurrentValue{std::move(value)} {}
22
23 virtual ~SqpIterator() noexcept = default;
24 SqpIterator(const SqpIterator &) = default;
25 SqpIterator(SqpIterator &&) = default;
26 SqpIterator &operator=(const SqpIterator &) = default;
27 SqpIterator &operator=(SqpIterator &&) = default;
28
29 SqpIterator &operator++()
30 {
31 m_CurrentValue.next();
32 return *this;
33 }
34
35 SqpIterator &operator--()
36 {
37 m_CurrentValue.prev();
38 return *this;
39 }
40
41 pointer operator->() const { return &m_CurrentValue; }
42 reference operator*() const { return m_CurrentValue; }
43
44 bool operator==(const SqpIterator &other) const
45 {
46 return m_CurrentValue.equals(other.m_CurrentValue);
47 }
48 bool operator!=(const SqpIterator &other) const { return !(*this == other); }
49
50 private:
51 T m_CurrentValue;
52 };
53
54 #endif // SCIQLOP_SQPITERATOR_H
@@ -0,0 +1,52
1 #include "Data/ArrayDataIterator.h"
2
3 ArrayDataIteratorValue::ArrayDataIteratorValue(std::unique_ptr<ArrayDataIteratorValue::Impl> impl)
4 : m_Impl{std::move(impl)}
5 {
6 }
7
8 ArrayDataIteratorValue::ArrayDataIteratorValue(const ArrayDataIteratorValue &other)
9 : m_Impl{other.m_Impl->clone()}
10 {
11 }
12
13 ArrayDataIteratorValue &ArrayDataIteratorValue::operator=(ArrayDataIteratorValue other)
14 {
15 std::swap(m_Impl, other.m_Impl);
16 return *this;
17 }
18
19 bool ArrayDataIteratorValue::equals(const ArrayDataIteratorValue &other) const
20 {
21 return m_Impl->equals(*other.m_Impl);
22 }
23
24 void ArrayDataIteratorValue::next()
25 {
26 m_Impl->next();
27 }
28
29 void ArrayDataIteratorValue::prev()
30 {
31 m_Impl->prev();
32 }
33
34 double ArrayDataIteratorValue::at(int componentIndex) const
35 {
36 return m_Impl->at(componentIndex);
37 }
38
39 double ArrayDataIteratorValue::first() const
40 {
41 return m_Impl->first();
42 }
43
44 double ArrayDataIteratorValue::min() const
45 {
46 return m_Impl->min();
47 }
48
49 double ArrayDataIteratorValue::max() const
50 {
51 return m_Impl->max();
52 }
@@ -37,7 +37,23 struct SortUtils {
37 }
37 }
38
38
39 /**
39 /**
40 * Sorts a container according to indices passed in parameter
40 * Sorts a container according to indices passed in parameter. The number of data in the
41 * container must be a multiple of the number of indices used to sort the container.
42 *
43 * Example 1:
44 * container: {1, 2, 3, 4, 5, 6}
45 * sortPermutation: {1, 0}
46 *
47 * Values will be sorted three by three, and the result will be:
48 * {4, 5, 6, 1, 2, 3}
49 *
50 * Example 2:
51 * container: {1, 2, 3, 4, 5, 6}
52 * sortPermutation: {2, 0, 1}
53 *
54 * Values will be sorted two by two, and the result will be:
55 * {5, 6, 1, 2, 3, 4}
56 *
41 * @param container the container sorted
57 * @param container the container sorted
42 * @param sortPermutation the indices used to sort the container
58 * @param sortPermutation the indices used to sort the container
43 * @return the container sorted
59 * @return the container sorted
@@ -45,18 +61,24 struct SortUtils {
45 * indices and its range is [0 ; vector.size()[ )
61 * indices and its range is [0 ; vector.size()[ )
46 */
62 */
47 template <typename Container>
63 template <typename Container>
48 static Container sort(const Container &container, const std::vector<int> &sortPermutation)
64 static Container sort(const Container &container, int nbValues,
65 const std::vector<int> &sortPermutation)
49 {
66 {
50 if (container.size() != sortPermutation.size()) {
67 auto containerSize = container.size();
68 if (containerSize % nbValues != 0
69 || ((containerSize / nbValues) != sortPermutation.size())) {
51 return Container{};
70 return Container{};
52 }
71 }
53
72
54 // Inits result
73 // Inits result
55 auto sortedData = Container{};
74 auto sortedData = Container{};
56 sortedData.resize(container.size());
75 sortedData.reserve(containerSize);
57
76
58 std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(),
77 for (auto i = 0, componentIndex = 0, permutationIndex = 0; i < containerSize;
59 [&container](int i) { return container.at(i); });
78 ++i, componentIndex = i % nbValues, permutationIndex = i / nbValues) {
79 auto insertIndex = sortPermutation.at(permutationIndex) * nbValues + componentIndex;
80 sortedData.append(container.at(insertIndex));
81 }
60
82
61 return sortedData;
83 return sortedData;
62 }
84 }
@@ -1,6 +1,7
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include "Data/ArrayDataIterator.h"
4 #include <Common/SortUtils.h>
5 #include <Common/SortUtils.h>
5
6
6 #include <QReadLocker>
7 #include <QReadLocker>
@@ -12,146 +13,110
12 template <int Dim>
13 template <int Dim>
13 class ArrayData;
14 class ArrayData;
14
15
15 using DataContainer = QVector<QVector<double> >;
16 using DataContainer = QVector<double>;
16
17
17 namespace arraydata_detail {
18 namespace arraydata_detail {
18
19
19 /// Struct used to sort ArrayData
20 /// Struct used to sort ArrayData
20 template <int Dim>
21 template <int Dim>
21 struct Sort {
22 struct Sort {
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
23 const std::vector<int> &sortPermutation)
24 const std::vector<int> &sortPermutation)
24 {
25 {
25 auto nbComponents = data.size();
26 return std::make_shared<ArrayData<Dim> >(
26 auto sortedData = DataContainer(nbComponents);
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
27
28 for (auto i = 0; i < nbComponents; ++i) {
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
30 }
31
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
33 }
28 }
34 };
29 };
35
30
36 /// Specialization for uni-dimensional ArrayData
31 /// Specialization for uni-dimensional ArrayData
37 template <>
32 template <>
38 struct Sort<1> {
33 struct Sort<1> {
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
40 const std::vector<int> &sortPermutation)
35 const std::vector<int> &sortPermutation)
41 {
36 {
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
37 Q_UNUSED(nbComponents)
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
43 }
39 }
44 };
40 };
45
41
46 } // namespace arraydata_detail
47
48 /**
49 * @brief The ArrayData class represents a dataset for a data series.
50 *
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
53 * number of values
54 *
55 * @tparam Dim the dimension of the ArrayData (one or two)
56 * @sa IDataSeries
57 */
58 template <int Dim>
42 template <int Dim>
59 class ArrayData {
43 class IteratorValue : public ArrayDataIteratorValue::Impl {
60 public:
61 class IteratorValue {
62 public:
44 public:
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
45 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
46 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
64 {
47 {
65 for (auto i = 0; i < container.size(); ++i) {
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
67 }
48 }
68 }
69
70 double at(int index) const { return *m_Its.at(index); }
71 double first() const { return *m_Its.front(); }
72
49
73 /// @return the min value among all components
50 IteratorValue(const IteratorValue &other) = default;
74 double min() const
75 {
76 auto end = m_Its.cend();
77 auto it = std::min_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
78 return SortUtils::minCompareWithNaN(*it1, *it2);
79 });
80 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
81 }
82
83 /// @return the max value among all components
84 double max() const
85 {
86 auto end = m_Its.cend();
87 auto it = std::max_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
88 return SortUtils::maxCompareWithNaN(*it1, *it2);
89 });
90 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
91 }
92
51
93 void next()
52 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
94 {
53 {
95 for (auto &it : m_Its) {
54 return std::make_unique<IteratorValue<Dim> >(*this);
96 ++it;
97 }
98 }
55 }
99
56
100 void prev()
57 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
101 {
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
102 for (auto &it : m_Its) {
59 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
103 --it;
104 }
60 }
61 catch (const std::bad_cast &) {
62 return false;
105 }
63 }
106
64
107 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
65 void next() override { std::advance(m_It, m_NbComponents); }
108
66 void prev() override { std::advance(m_It, -m_NbComponents); }
109 private:
110 std::vector<DataContainer::value_type::const_iterator> m_Its;
111 };
112
113 class Iterator {
114 public:
115 using iterator_category = std::forward_iterator_tag;
116 using value_type = const IteratorValue;
117 using difference_type = std::ptrdiff_t;
118 using pointer = value_type *;
119 using reference = value_type &;
120
67
121 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
68 double at(int componentIndex) const override { return *(m_It + componentIndex); }
122
69 double first() const override { return *m_It; }
123 virtual ~Iterator() noexcept = default;
70 double min() const override
124 Iterator(const Iterator &) = default;
125 Iterator(Iterator &&) = default;
126 Iterator &operator=(const Iterator &) = default;
127 Iterator &operator=(Iterator &&) = default;
128
129 Iterator &operator++()
130 {
71 {
131 m_CurrentValue.next();
72 auto values = this->values();
132 return *this;
73 auto end = values.cend();
133 }
74 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
75 return SortUtils::minCompareWithNaN(v1, v2);
76 });
134
77
135 Iterator &operator--()
78 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
79 }
80 double max() const override
136 {
81 {
137 m_CurrentValue.prev();
82 auto values = this->values();
138 return *this;
83 auto end = values.cend();
84 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
85 return SortUtils::maxCompareWithNaN(v1, v2);
86 });
87 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
139 }
88 }
140
89
141 pointer operator->() const { return &m_CurrentValue; }
90 private:
142 reference operator*() const { return m_CurrentValue; }
91 std::vector<double> values() const
143
144 bool operator==(const Iterator &other) const
145 {
92 {
146 return m_CurrentValue == other.m_CurrentValue;
93 auto result = std::vector<double>{};
94 for (auto i = 0; i < m_NbComponents; ++i) {
95 result.push_back(*(m_It + i));
147 }
96 }
148
97
149 bool operator!=(const Iterator &other) const { return !(*this == other); }
98 return result;
99 }
150
100
151 private:
101 DataContainer::const_iterator m_It;
152 IteratorValue m_CurrentValue;
102 int m_NbComponents;
153 };
103 };
154
104
105 } // namespace arraydata_detail
106
107 /**
108 * @brief The ArrayData class represents a dataset for a data series.
109 *
110 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
111 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
112 * number of values
113 *
114 * @tparam Dim the dimension of the ArrayData (one or two)
115 * @sa IDataSeries
116 */
117 template <int Dim>
118 class ArrayData {
119 public:
155 // ///// //
120 // ///// //
156 // Ctors //
121 // Ctors //
157 // ///// //
122 // ///// //
@@ -161,37 +126,34 public:
161 * @param data the data the ArrayData will hold
126 * @param data the data the ArrayData will hold
162 */
127 */
163 template <int D = Dim, typename = std::enable_if_t<D == 1> >
128 template <int D = Dim, typename = std::enable_if_t<D == 1> >
164 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
129 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
165 {
130 {
166 m_Data[0] = std::move(data);
167 }
131 }
168
132
169 /**
133 /**
170 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
134 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
171 * greater than 2 and each component must have the same number of values
135 * greater than 2 and must be a divisor of the total number of data in the vector
172 * @param data the data the ArrayData will hold
136 * @param data the data the ArrayData will hold
173 * @throws std::invalid_argument if the number of components is less than 2
137 * @param nbComponents the number of components
174 * @remarks if the number of values is not the same for each component, no value is set
138 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
139 * of the size of the data
175 */
140 */
176 template <int D = Dim, typename = std::enable_if_t<D == 2> >
141 template <int D = Dim, typename = std::enable_if_t<D == 2> >
177 explicit ArrayData(DataContainer data)
142 explicit ArrayData(DataContainer data, int nbComponents)
143 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
178 {
144 {
179 auto nbComponents = data.size();
180 if (nbComponents < 2) {
145 if (nbComponents < 2) {
181 throw std::invalid_argument{
146 throw std::invalid_argument{
182 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
147 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
183 .arg(data.size())
148 .arg(nbComponents)
184 .toStdString()};
149 .toStdString()};
185 }
150 }
186
151
187 auto nbValues = data.front().size();
152 if (m_Data.size() % m_NbComponents != 0) {
188 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
153 throw std::invalid_argument{QString{
189 return component.size() == nbValues;
154 "The number of components (%1) is inconsistent with the total number of data (%2)"}
190 })) {
155 .arg(m_Data.size(), nbComponents)
191 m_Data = std::move(data);
156 .toStdString()};
192 }
193 else {
194 m_Data = DataContainer{nbComponents, QVector<double>{}};
195 }
157 }
196 }
158 }
197
159
@@ -200,6 +162,7 public:
200 {
162 {
201 QReadLocker otherLocker{&other.m_Lock};
163 QReadLocker otherLocker{&other.m_Lock};
202 m_Data = other.m_Data;
164 m_Data = other.m_Data;
165 m_NbComponents = other.m_NbComponents;
203 }
166 }
204
167
205 // /////////////// //
168 // /////////////// //
@@ -218,91 +181,75 public:
218 QWriteLocker locker{&m_Lock};
181 QWriteLocker locker{&m_Lock};
219 QReadLocker otherLocker{&other.m_Lock};
182 QReadLocker otherLocker{&other.m_Lock};
220
183
221 auto nbComponents = m_Data.size();
184 if (m_NbComponents != other.componentCount()) {
222 if (nbComponents != other.m_Data.size()) {
223 return;
185 return;
224 }
186 }
225
187
226 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
227 if (prepend) {
188 if (prepend) {
228 const auto &otherData = other.data(componentIndex);
189 auto otherDataSize = other.m_Data.size();
229 const auto otherDataSize = otherData.size();
190 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
230
231 auto &data = m_Data[componentIndex];
232 data.insert(data.begin(), otherDataSize, 0.);
233
234 for (auto i = 0; i < otherDataSize; ++i) {
191 for (auto i = 0; i < otherDataSize; ++i) {
235 data.replace(i, otherData.at(i));
192 m_Data.replace(i, other.m_Data.at(i));
236 }
193 }
237 }
194 }
238 else {
195 else {
239 m_Data[componentIndex] += other.data(componentIndex);
196 m_Data.append(other.m_Data);
240 }
241 }
197 }
242 }
198 }
243
199
244 void clear()
200 void clear()
245 {
201 {
246 QWriteLocker locker{&m_Lock};
202 QWriteLocker locker{&m_Lock};
247
203 m_Data.clear();
248 auto nbComponents = m_Data.size();
249 for (auto i = 0; i < nbComponents; ++i) {
250 m_Data[i].clear();
251 }
252 }
204 }
253
205
254 int componentCount() const noexcept { return m_Data.size(); }
206 int componentCount() const noexcept { return m_NbComponents; }
255
256 /**
257 * @return the data of a component
258 * @param componentIndex the index of the component to retrieve the data
259 * @return the component's data, empty vector if the index is invalid
260 */
261 QVector<double> data(int componentIndex) const noexcept
262 {
263 QReadLocker locker{&m_Lock};
264
265 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
266 : QVector<double>{};
267 }
268
207
269 /// @return the size (i.e. number of values) of a single component
208 /// @return the size (i.e. number of values) of a single component
270 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
209 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
271 int size() const
210 int size() const
272 {
211 {
273 QReadLocker locker{&m_Lock};
212 QReadLocker locker{&m_Lock};
274 return m_Data[0].size();
213 return m_Data.size() / m_NbComponents;
275 }
214 }
276
215
277 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
216 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
278 {
217 {
279 QReadLocker locker{&m_Lock};
218 QReadLocker locker{&m_Lock};
280 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
219 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
281 }
220 }
282
221
283 // ///////// //
222 // ///////// //
284 // Iterators //
223 // Iterators //
285 // ///////// //
224 // ///////// //
286
225
287 Iterator cbegin() const { return Iterator{m_Data, true}; }
226 ArrayDataIterator cbegin() const
288 Iterator cend() const { return Iterator{m_Data, false}; }
227 {
228 return ArrayDataIterator{ArrayDataIteratorValue{
229 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, m_NbComponents, true)}};
230 }
231 ArrayDataIterator cend() const
232 {
233 return ArrayDataIterator{
234 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim> >(
235 m_Data, m_NbComponents, false)}};
236 }
289
237
290 // ///////////// //
291 // 1-dim methods //
292 // ///////////// //
293
238
294 /**
239 /**
295 * @return the data at a specified index
240 * @return the data at a specified index
296 * @remarks index must be a valid position
241 * @remarks index must be a valid position
297 * @remarks this method is only available for a unidimensional ArrayData
298 */
242 */
299 template <int D = Dim, typename = std::enable_if_t<D == 1> >
300 double at(int index) const noexcept
243 double at(int index) const noexcept
301 {
244 {
302 QReadLocker locker{&m_Lock};
245 QReadLocker locker{&m_Lock};
303 return m_Data[0].at(index);
246 return m_Data.at(index);
304 }
247 }
305
248
249 // ///////////// //
250 // 1-dim methods //
251 // ///////////// //
252
306 /**
253 /**
307 * @return the data as a vector, as a const reference
254 * @return the data as a vector, as a const reference
308 * @remarks this method is only available for a unidimensional ArrayData
255 * @remarks this method is only available for a unidimensional ArrayData
@@ -311,37 +258,13 public:
311 const QVector<double> &cdata() const noexcept
258 const QVector<double> &cdata() const noexcept
312 {
259 {
313 QReadLocker locker{&m_Lock};
260 QReadLocker locker{&m_Lock};
314 return m_Data.at(0);
315 }
316
317 /**
318 * @return the data as a vector
319 * @remarks this method is only available for a unidimensional ArrayData
320 */
321 template <int D = Dim, typename = std::enable_if_t<D == 1> >
322 QVector<double> data() const noexcept
323 {
324 QReadLocker locker{&m_Lock};
325 return m_Data[0];
326 }
327
328 // ///////////// //
329 // 2-dim methods //
330 // ///////////// //
331
332 /**
333 * @return the data
334 * @remarks this method is only available for a two-dimensional ArrayData
335 */
336 template <int D = Dim, typename = std::enable_if_t<D == 2> >
337 DataContainer data() const noexcept
338 {
339 QReadLocker locker{&m_Lock};
340 return m_Data;
261 return m_Data;
341 }
262 }
342
263
343 private:
264 private:
344 DataContainer m_Data;
265 DataContainer m_Data;
266 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
267 int m_NbComponents;
345 mutable QReadWriteLock m_Lock;
268 mutable QReadWriteLock m_Lock;
346 };
269 };
347
270
@@ -69,8 +69,8 public:
69 double maxValue() const override { return m_ValuesIt->max(); }
69 double maxValue() const override { return m_ValuesIt->max(); }
70
70
71 private:
71 private:
72 ArrayData<1>::Iterator m_XIt;
72 ArrayDataIterator m_XIt;
73 typename ArrayData<Dim>::Iterator m_ValuesIt;
73 ArrayDataIterator m_ValuesIt;
74 };
74 };
75 } // namespace dataseries_detail
75 } // namespace dataseries_detail
76
76
@@ -2,6 +2,7
2 #define SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
5
6
6 #include <memory>
7 #include <memory>
7
8
@@ -54,35 +55,6 private:
54 std::unique_ptr<Impl> m_Impl;
55 std::unique_ptr<Impl> m_Impl;
55 };
56 };
56
57
57 /**
58 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
58 * @brief The DataSeriesIterator class represents an iterator used for data series. It defines all
59 * operators needed for a standard forward iterator
60 * @sa http://www.cplusplus.com/reference/iterator/
61 */
62 class SCIQLOP_CORE_EXPORT DataSeriesIterator {
63 public:
64 using iterator_category = std::forward_iterator_tag;
65 using value_type = const DataSeriesIteratorValue;
66 using difference_type = std::ptrdiff_t;
67 using pointer = value_type *;
68 using reference = value_type &;
69
70 explicit DataSeriesIterator(DataSeriesIteratorValue value);
71 virtual ~DataSeriesIterator() noexcept = default;
72 DataSeriesIterator(const DataSeriesIterator &) = default;
73 DataSeriesIterator(DataSeriesIterator &&) = default;
74 DataSeriesIterator &operator=(const DataSeriesIterator &) = default;
75 DataSeriesIterator &operator=(DataSeriesIterator &&) = default;
76
77 DataSeriesIterator &operator++();
78 DataSeriesIterator &operator--();
79 pointer operator->() const { return &m_CurrentValue; }
80 reference operator*() const { return m_CurrentValue; }
81 bool operator==(const DataSeriesIterator &other) const;
82 bool operator!=(const DataSeriesIterator &other) const;
83
84 private:
85 DataSeriesIteratorValue m_CurrentValue;
86 };
87
59
88 #endif // SCIQLOP_DATASERIESITERATOR_H
60 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -11,8 +11,8
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
12 public:
12 public:
13 /**
13 /**
14 * Ctor. The vectors must have the same size, otherwise a ScalarSeries with no values will be
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
15 * created.
15 * ScalarSeries with no values will be created.
16 * @param xAxisData x-axis data
16 * @param xAxisData x-axis data
17 * @param xvaluesData x-values data
17 * @param xvaluesData x-values data
18 * @param yvaluesData y-values data
18 * @param yvaluesData y-values data
@@ -22,6 +22,10 public:
22 QVector<double> yValuesData, QVector<double> zValuesData,
22 QVector<double> yValuesData, QVector<double> zValuesData,
23 const Unit &xAxisUnit, const Unit &valuesUnit);
23 const Unit &xAxisUnit, const Unit &valuesUnit);
24
24
25 /// Default Ctor
26 explicit VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
27 const Unit &xAxisUnit, const Unit &valuesUnit);
28
25 std::unique_ptr<IDataSeries> clone() const;
29 std::unique_ptr<IDataSeries> clone() const;
26
30
27 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
31 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
@@ -56,30 +56,3 double DataSeriesIteratorValue::maxValue() const
56 {
56 {
57 return m_Impl->maxValue();
57 return m_Impl->maxValue();
58 }
58 }
59
60 DataSeriesIterator::DataSeriesIterator(DataSeriesIteratorValue value)
61 : m_CurrentValue{std::move(value)}
62 {
63 }
64
65 DataSeriesIterator &DataSeriesIterator::operator++()
66 {
67 m_CurrentValue.next();
68 return *this;
69 }
70
71 DataSeriesIterator &DataSeriesIterator::operator--()
72 {
73 m_CurrentValue.prev();
74 return *this;
75 }
76
77 bool DataSeriesIterator::operator==(const DataSeriesIterator &other) const
78 {
79 return m_CurrentValue.equals(other.m_CurrentValue);
80 }
81
82 bool DataSeriesIterator::operator!=(const DataSeriesIterator &other) const
83 {
84 return !(*this == other);
85 }
@@ -1,12 +1,56
1 #include "Data/VectorSeries.h"
1 #include "Data/VectorSeries.h"
2
2
3 namespace {
4
5 /**
6 * Flatten the three components of a vector to a single QVector that can be passed to an ArrayData
7 *
8 * Example:
9 * xValues = {1, 2, 3}
10 * yValues = {4, 5, 6}
11 * zValues = {7, 8, 9}
12 *
13 * result = {1, 4, 7, 2, 5, 8, 3, 6, 9}
14 *
15 * @param xValues the x-component values of the vector
16 * @param yValues the y-component values of the vector
17 * @param zValues the z-component values of the vector
18 * @return the single QVector
19 * @remarks the three components are consumed
20 * @sa ArrayData
21 */
22 QVector<double> flatten(QVector<double> xValues, QVector<double> yValues, QVector<double> zValues)
23 {
24 if (xValues.size() != yValues.size() || xValues.size() != zValues.size()) {
25 /// @todo ALX : log
26 return {};
27 }
28
29 auto result = QVector<double>{};
30 result.reserve(xValues.size() * 3);
31
32 while (!xValues.isEmpty()) {
33 result.append({xValues.takeFirst(), yValues.takeFirst(), zValues.takeFirst()});
34 }
35
36 return result;
37 }
38
39 } // namespace
40
3 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
41 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
4 QVector<double> yValuesData, QVector<double> zValuesData,
42 QVector<double> yValuesData, QVector<double> zValuesData,
5 const Unit &xAxisUnit, const Unit &valuesUnit)
43 const Unit &xAxisUnit, const Unit &valuesUnit)
44 : VectorSeries{std::move(xAxisData), flatten(std::move(xValuesData), std::move(yValuesData),
45 std::move(zValuesData)),
46 xAxisUnit, valuesUnit}
47 {
48 }
49
50 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
51 const Unit &xAxisUnit, const Unit &valuesUnit)
6 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
52 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
7 std::make_shared<ArrayData<2> >(QVector<QVector<double> >{
53 std::make_shared<ArrayData<2> >(std::move(valuesData), 3), valuesUnit}
8 std::move(xValuesData), std::move(yValuesData), std::move(zValuesData)}),
9 valuesUnit}
10 {
54 {
11 }
55 }
12
56
@@ -10,6 +10,20
10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12
12
13 namespace {
14
15 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const QVector<double> &xData,
16 const QVector<double> &valuesData)
17 {
18 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
19 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
20 QVERIFY(std::equal(
21 first, last, valuesData.cbegin(), valuesData.cend(),
22 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
23 }
24
25 } // namespace
26
13 class TestDataSeries : public QObject {
27 class TestDataSeries : public QObject {
14 Q_OBJECT
28 Q_OBJECT
15 private:
29 private:
@@ -164,13 +178,7 void TestDataSeries::testCtor()
164 QFETCH(QVector<double>, expectedXAxisData);
178 QFETCH(QVector<double>, expectedXAxisData);
165 QFETCH(QVector<double>, expectedValuesData);
179 QFETCH(QVector<double>, expectedValuesData);
166
180
167 auto seriesXAxisData = series->xAxisData()->data();
181 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
168 auto seriesValuesData = series->valuesData()->data();
169
170 QVERIFY(
171 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
172 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
173 seriesValuesData.cbegin()));
174 }
182 }
175
183
176 namespace {
184 namespace {
@@ -250,13 +258,7 void TestDataSeries::testMerge()
250 QFETCH(QVector<double>, expectedXAxisData);
258 QFETCH(QVector<double>, expectedXAxisData);
251 QFETCH(QVector<double>, expectedValuesData);
259 QFETCH(QVector<double>, expectedValuesData);
252
260
253 auto seriesXAxisData = dataSeries->xAxisData()->data();
261 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
254 auto seriesValuesData = dataSeries->valuesData()->data();
255
256 QVERIFY(
257 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
258 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
259 seriesValuesData.cbegin()));
260 }
262 }
261
263
262 void TestDataSeries::testMinXAxisData_data()
264 void TestDataSeries::testMinXAxisData_data()
@@ -438,12 +440,7 void TestDataSeries::testXAxisRange()
438 QFETCH(QVector<double>, expectedValuesData);
440 QFETCH(QVector<double>, expectedValuesData);
439
441
440 auto bounds = dataSeries->xAxisRange(min, max);
442 auto bounds = dataSeries->xAxisRange(min, max);
441 QVERIFY(std::equal(bounds.first, bounds.second, expectedXAxisData.cbegin(),
443 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
442 expectedXAxisData.cend(),
443 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
444 QVERIFY(std::equal(
445 bounds.first, bounds.second, expectedValuesData.cbegin(), expectedValuesData.cend(),
446 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
447 }
444 }
448
445
449 void TestDataSeries::testValuesBoundsScalar_data()
446 void TestDataSeries::testValuesBoundsScalar_data()
@@ -2,6 +2,17
2 #include <QObject>
2 #include <QObject>
3 #include <QtTest>
3 #include <QtTest>
4
4
5 namespace {
6
7 void verifyArrayData(const ArrayData<1> &arrayData, const QVector<double> &expectedData)
8 {
9 QVERIFY(std::equal(
10 arrayData.cbegin(), arrayData.cend(), expectedData.cbegin(), expectedData.cend(),
11 [](const auto &it, const auto &expectedData) { return it.at(0) == expectedData; }));
12 }
13
14 } // namespace
15
5 class TestOneDimArrayData : public QObject {
16 class TestOneDimArrayData : public QObject {
6 Q_OBJECT
17 Q_OBJECT
7 private slots:
18 private slots:
@@ -9,10 +20,6 private slots:
9 void testData_data();
20 void testData_data();
10 void testData();
21 void testData();
11
22
12 /// Tests @sa ArrayData::data(int componentIndex)
13 void testDataByComponentIndex_data();
14 void testDataByComponentIndex();
15
16 /// Tests @sa ArrayData::add()
23 /// Tests @sa ArrayData::add()
17 void testAdd_data();
24 void testAdd_data();
18 void testAdd();
25 void testAdd();
@@ -51,32 +58,7 void TestOneDimArrayData::testData()
51 QFETCH(QVector<double>, expectedData);
58 QFETCH(QVector<double>, expectedData);
52
59
53 ArrayData<1> arrayData{inputData};
60 ArrayData<1> arrayData{inputData};
54 QVERIFY(arrayData.data() == expectedData);
61 verifyArrayData(arrayData, expectedData);
55 }
56
57 void TestOneDimArrayData::testDataByComponentIndex_data()
58 {
59 // Test structure
60 QTest::addColumn<QVector<double> >("inputData"); // array data's input
61 QTest::addColumn<int>("componentIndex"); // component index to test
62 QTest::addColumn<QVector<double> >("expectedData"); // expected data
63
64 // Test cases
65 QTest::newRow("validIndex") << QVector<double>{1., 2., 3., 4., 5.} << 0
66 << QVector<double>{1., 2., 3., 4., 5.};
67 QTest::newRow("invalidIndex1") << QVector<double>{1., 2., 3., 4., 5.} << -1
68 << QVector<double>{};
69 QTest::newRow("invalidIndex2") << QVector<double>{1., 2., 3., 4., 5.} << 1 << QVector<double>{};
70 }
71
72 void TestOneDimArrayData::testDataByComponentIndex()
73 {
74 QFETCH(QVector<double>, inputData);
75 QFETCH(int, componentIndex);
76 QFETCH(QVector<double>, expectedData);
77
78 ArrayData<1> arrayData{inputData};
79 QVERIFY(arrayData.data(componentIndex) == expectedData);
80 }
62 }
81
63
82 void TestOneDimArrayData::testAdd_data()
64 void TestOneDimArrayData::testAdd_data()
@@ -107,7 +89,7 void TestOneDimArrayData::testAdd()
107 ArrayData<1> other{otherData};
89 ArrayData<1> other{otherData};
108
90
109 arrayData.add(other, prepend);
91 arrayData.add(other, prepend);
110 QVERIFY(arrayData.data() == expectedData);
92 verifyArrayData(arrayData, expectedData);
111 }
93 }
112
94
113 void TestOneDimArrayData::testAt_data()
95 void TestOneDimArrayData::testAt_data()
@@ -147,7 +129,7 void TestOneDimArrayData::testClear()
147
129
148 ArrayData<1> arrayData{inputData};
130 ArrayData<1> arrayData{inputData};
149 arrayData.clear();
131 arrayData.clear();
150 QVERIFY(arrayData.data() == QVector<double>{});
132 verifyArrayData(arrayData, QVector<double>{});
151 }
133 }
152
134
153 void TestOneDimArrayData::testSize_data()
135 void TestOneDimArrayData::testSize_data()
@@ -192,7 +174,7 void TestOneDimArrayData::testSort()
192 ArrayData<1> arrayData{inputData};
174 ArrayData<1> arrayData{inputData};
193 auto sortedArrayData = arrayData.sort(sortPermutation);
175 auto sortedArrayData = arrayData.sort(sortPermutation);
194 QVERIFY(sortedArrayData != nullptr);
176 QVERIFY(sortedArrayData != nullptr);
195 QVERIFY(sortedArrayData->data() == expectedData);
177 verifyArrayData(*sortedArrayData, expectedData);
196 }
178 }
197
179
198 QTEST_MAIN(TestOneDimArrayData)
180 QTEST_MAIN(TestOneDimArrayData)
@@ -2,15 +2,55
2 #include <QObject>
2 #include <QObject>
3 #include <QtTest>
3 #include <QtTest>
4
4
5 using DataContainer = QVector<QVector<double> >;
5 using Container = QVector<QVector<double> >;
6 using InputData = QPair<QVector<double>, int>;
7
8 namespace {
9
10 InputData flatten(const Container &container)
11 {
12 if (container.isEmpty()) {
13 return {};
14 }
15
16 // We assume here that each component of the container have the same size
17 auto containerSize = container.size();
18 auto componentSize = container.first().size();
19
20 auto result = QVector<double>{};
21 result.reserve(componentSize * containerSize);
22
23 for (auto i = 0; i < componentSize; ++i) {
24 for (auto j = 0; j < containerSize; ++j) {
25 result.append(container.at(j).at(i));
26 }
27 }
28
29 return {result, containerSize};
30 }
31
32 void verifyArrayData(const ArrayData<2> &arrayData, const Container &expectedData)
33 {
34 auto verifyComponent = [&arrayData](const auto &componentData, const auto &equalFun) {
35 QVERIFY(std::equal(arrayData.cbegin(), arrayData.cend(), componentData.cbegin(),
36 componentData.cend(),
37 [&equalFun](const auto &dataSeriesIt, const auto &expectedValue) {
38 return equalFun(dataSeriesIt, expectedValue);
39 }));
40 };
41
42 for (auto i = 0; i < expectedData.size(); ++i) {
43 verifyComponent(expectedData.at(i), [i](const auto &seriesIt, const auto &value) {
44 return seriesIt.at(i) == value;
45 });
46 }
47 }
48
49 } // namespace
6
50
7 class TestTwoDimArrayData : public QObject {
51 class TestTwoDimArrayData : public QObject {
8 Q_OBJECT
52 Q_OBJECT
9 private slots:
53 private slots:
10 /// Tests @sa ArrayData::data(int componentIndex)
11 void testDataByComponentIndex_data();
12 void testDataByComponentIndex();
13
14 /// Tests @sa ArrayData ctor
54 /// Tests @sa ArrayData ctor
15 void testCtor_data();
55 void testCtor_data();
16 void testCtor();
56 void testCtor();
@@ -32,192 +72,167 private slots:
32 void testSort();
72 void testSort();
33 };
73 };
34
74
35 void TestTwoDimArrayData::testDataByComponentIndex_data()
36 {
37 // Test structure
38 QTest::addColumn<DataContainer>("inputData"); // array data's input
39 QTest::addColumn<int>("componentIndex"); // component index to test
40 QTest::addColumn<QVector<double> >("expectedData"); // expected data
41
42 // Test cases
43 auto inputData
44 = DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
45
46 QTest::newRow("validIndex1") << inputData << 0 << QVector<double>{1., 2., 3., 4., 5.};
47 QTest::newRow("validIndex2") << inputData << 1 << QVector<double>{6., 7., 8., 9., 10.};
48 QTest::newRow("validIndex3") << inputData << 2 << QVector<double>{11., 12., 13., 14., 15.};
49 QTest::newRow("invalidIndex1") << inputData << -1 << QVector<double>{};
50 QTest::newRow("invalidIndex2") << inputData << 3 << QVector<double>{};
51 }
52
53 void TestTwoDimArrayData::testDataByComponentIndex()
54 {
55 QFETCH(DataContainer, inputData);
56 QFETCH(int, componentIndex);
57 QFETCH(QVector<double>, expectedData);
58
59 ArrayData<2> arrayData{inputData};
60 QVERIFY(arrayData.data(componentIndex) == expectedData);
61 }
62
63 void TestTwoDimArrayData::testCtor_data()
75 void TestTwoDimArrayData::testCtor_data()
64 {
76 {
65 // Test structure
77 // Test structure
66 QTest::addColumn<DataContainer>("inputData"); // array data's input
78 QTest::addColumn<InputData>("inputData"); // array data's input
67 QTest::addColumn<bool>("success"); // array data has been successfully constructed
79 QTest::addColumn<bool>("success"); // array data has been successfully constructed
68 QTest::addColumn<DataContainer>("expectedData"); // expected array data (when success)
80 QTest::addColumn<Container>("expectedData"); // expected array data (when success)
69
81
70 // Test cases
82 // Test cases
71 QTest::newRow("validInput")
83 QTest::newRow("validInput") << flatten(Container{{1., 2., 3., 4., 5.},
72 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
84 {6., 7., 8., 9., 10.},
73 << true
85 {11., 12., 13., 14., 15.}})
74 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
86 << true << Container{{1., 2., 3., 4., 5.},
75 QTest::newRow("malformedInput (components of the array data haven't the same size")
87 {6., 7., 8., 9., 10.},
76 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8.}, {11., 12.}} << true
88 {11., 12., 13., 14., 15.}};
77 << DataContainer{{}, {}, {}};
89 QTest::newRow("invalidInput (invalid data size")
78 QTest::newRow("invalidInput (less than tow components") << DataContainer{{1., 2., 3., 4., 5.}}
90 << InputData{{1., 2., 3., 4., 5., 6., 7.}, 3} << false << Container{{}, {}, {}};
79 << false << DataContainer{{}, {}, {}};
91 QTest::newRow("invalidInput (less than two components")
92 << flatten(Container{{1., 2., 3., 4., 5.}}) << false << Container{{}, {}, {}};
80 }
93 }
81
94
82 void TestTwoDimArrayData::testCtor()
95 void TestTwoDimArrayData::testCtor()
83 {
96 {
84 QFETCH(DataContainer, inputData);
97 QFETCH(InputData, inputData);
85 QFETCH(bool, success);
98 QFETCH(bool, success);
86
99
87 if (success) {
100 if (success) {
88 QFETCH(DataContainer, expectedData);
101 QFETCH(Container, expectedData);
89
90 ArrayData<2> arrayData{inputData};
91
102
92 for (auto i = 0; i < expectedData.size(); ++i) {
103 ArrayData<2> arrayData{inputData.first, inputData.second};
93 QVERIFY(arrayData.data(i) == expectedData.at(i));
104 verifyArrayData(arrayData, expectedData);
94 }
95 }
105 }
96 else {
106 else {
97 QVERIFY_EXCEPTION_THROWN(ArrayData<2> arrayData{inputData}, std::invalid_argument);
107 QVERIFY_EXCEPTION_THROWN(ArrayData<2>(inputData.first, inputData.second),
108 std::invalid_argument);
98 }
109 }
99 }
110 }
100
111
101 void TestTwoDimArrayData::testAdd_data()
112 void TestTwoDimArrayData::testAdd_data()
102 {
113 {
103 // Test structure
114 // Test structure
104 QTest::addColumn<DataContainer>("inputData"); // array's data input
115 QTest::addColumn<InputData>("inputData"); // array's data input
105 QTest::addColumn<DataContainer>("otherData"); // array data's input to merge with
116 QTest::addColumn<InputData>("otherData"); // array data's input to merge with
106 QTest::addColumn<bool>("prepend"); // prepend or append merge
117 QTest::addColumn<bool>("prepend"); // prepend or append merge
107 QTest::addColumn<DataContainer>("expectedData"); // expected data after merge
118 QTest::addColumn<Container>("expectedData"); // expected data after merge
108
119
109 // Test cases
120 // Test cases
110 auto inputData
121 auto inputData = flatten(
111 = DataContainer{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}};
122 Container{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}});
112
123
113 auto vectorContainer = DataContainer{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}};
124 auto vectorContainer = flatten(Container{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}});
114 auto tensorContainer = DataContainer{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28},
125 auto tensorContainer = flatten(Container{{6., 7., 8.},
115 {36., 37., 38.}, {46., 47., 48.}, {56., 57., 58}};
126 {16., 17., 18.},
127 {26., 27., 28},
128 {36., 37., 38.},
129 {46., 47., 48.},
130 {56., 57., 58}});
116
131
117 QTest::newRow("appendMerge") << inputData << vectorContainer << false
132 QTest::newRow("appendMerge") << inputData << vectorContainer << false
118 << DataContainer{{1., 2., 3., 4., 5., 6., 7., 8.},
133 << Container{{1., 2., 3., 4., 5., 6., 7., 8.},
119 {11., 12., 13., 14., 15., 16., 17., 18.},
134 {11., 12., 13., 14., 15., 16., 17., 18.},
120 {21., 22., 23., 24., 25., 26., 27., 28}};
135 {21., 22., 23., 24., 25., 26., 27., 28}};
121 QTest::newRow("prependMerge") << inputData << vectorContainer << true
136 QTest::newRow("prependMerge") << inputData << vectorContainer << true
122 << DataContainer{{6., 7., 8., 1., 2., 3., 4., 5.},
137 << Container{{6., 7., 8., 1., 2., 3., 4., 5.},
123 {16., 17., 18., 11., 12., 13., 14., 15.},
138 {16., 17., 18., 11., 12., 13., 14., 15.},
124 {26., 27., 28, 21., 22., 23., 24., 25.}};
139 {26., 27., 28, 21., 22., 23., 24., 25.}};
125 QTest::newRow("invalidMerge") << inputData << tensorContainer << false << inputData;
140 QTest::newRow("invalidMerge") << inputData << tensorContainer << false
141 << Container{{1., 2., 3., 4., 5.},
142 {11., 12., 13., 14., 15.},
143 {21., 22., 23., 24., 25.}};
126 }
144 }
127
145
128 void TestTwoDimArrayData::testAdd()
146 void TestTwoDimArrayData::testAdd()
129 {
147 {
130 QFETCH(DataContainer, inputData);
148 QFETCH(InputData, inputData);
131 QFETCH(DataContainer, otherData);
149 QFETCH(InputData, otherData);
132 QFETCH(bool, prepend);
150 QFETCH(bool, prepend);
133 QFETCH(DataContainer, expectedData);
151 QFETCH(Container, expectedData);
134
152
135 ArrayData<2> arrayData{inputData};
153 ArrayData<2> arrayData{inputData.first, inputData.second};
136 ArrayData<2> other{otherData};
154 ArrayData<2> other{otherData.first, otherData.second};
137
155
138 arrayData.add(other, prepend);
156 arrayData.add(other, prepend);
139
157
140 for (auto i = 0; i < expectedData.size(); ++i) {
158 verifyArrayData(arrayData, expectedData);
141 QVERIFY(arrayData.data(i) == expectedData.at(i));
142 }
143 }
159 }
144
160
145 void TestTwoDimArrayData::testClear_data()
161 void TestTwoDimArrayData::testClear_data()
146 {
162 {
147 // Test structure
163 // Test structure
148 QTest::addColumn<DataContainer>("inputData"); // array data's input
164 QTest::addColumn<InputData>("inputData"); // array data's input
149
165
150 // Test cases
166 // Test cases
151 QTest::newRow("data1") << DataContainer{
167 QTest::newRow("data1") << flatten(
152 {1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
168 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}});
153 }
169 }
154
170
155 void TestTwoDimArrayData::testClear()
171 void TestTwoDimArrayData::testClear()
156 {
172 {
157 QFETCH(DataContainer, inputData);
173 QFETCH(InputData, inputData);
158
174
159 ArrayData<2> arrayData{inputData};
175 ArrayData<2> arrayData{inputData.first, inputData.second};
160 arrayData.clear();
176 arrayData.clear();
161
177
162 for (auto i = 0; i < inputData.size(); ++i) {
178 auto emptyData = Container(inputData.second, QVector<double>{});
163 QVERIFY(arrayData.data(i) == QVector<double>{});
179 verifyArrayData(arrayData, emptyData);
164 }
165 }
180 }
166
181
167 void TestTwoDimArrayData::testSize_data()
182 void TestTwoDimArrayData::testSize_data()
168 {
183 {
169 // Test structure
184 // Test structure
170 QTest::addColumn<QVector<QVector<double> > >("inputData"); // array data's input
185 QTest::addColumn<InputData>("inputData"); // array data's input
171 QTest::addColumn<int>("expectedSize"); // expected array data size
186 QTest::addColumn<int>("expectedSize"); // expected array data size
172
187
173 // Test cases
188 // Test cases
174 QTest::newRow("data1") << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}} << 5;
189 QTest::newRow("data1") << flatten(Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}}) << 5;
175 QTest::newRow("data2") << DataContainer{{1., 2., 3., 4., 5.},
190 QTest::newRow("data2") << flatten(Container{{1., 2., 3., 4., 5.},
176 {6., 7., 8., 9., 10.},
191 {6., 7., 8., 9., 10.},
177 {11., 12., 13., 14., 15.}}
192 {11., 12., 13., 14., 15.}})
178 << 5;
193 << 5;
179 }
194 }
180
195
181 void TestTwoDimArrayData::testSize()
196 void TestTwoDimArrayData::testSize()
182 {
197 {
183 QFETCH(DataContainer, inputData);
198 QFETCH(InputData, inputData);
184 QFETCH(int, expectedSize);
199 QFETCH(int, expectedSize);
185
200
186 ArrayData<2> arrayData{inputData};
201 ArrayData<2> arrayData{inputData.first, inputData.second};
187 QVERIFY(arrayData.size() == expectedSize);
202 QVERIFY(arrayData.size() == expectedSize);
188 }
203 }
189
204
190 void TestTwoDimArrayData::testSort_data()
205 void TestTwoDimArrayData::testSort_data()
191 {
206 {
192 // Test structure
207 // Test structure
193 QTest::addColumn<DataContainer>("inputData"); // array data's input
208 QTest::addColumn<InputData>("inputData"); // array data's input
194 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
209 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
195 QTest::addColumn<DataContainer>("expectedData"); // expected data after sorting
210 QTest::addColumn<Container>("expectedData"); // expected data after sorting
196
211
197 // Test cases
212 // Test cases
198 QTest::newRow("data1")
213 QTest::newRow("data1")
199 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
214 << flatten(
215 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
200 << std::vector<int>{0, 2, 3, 1, 4}
216 << std::vector<int>{0, 2, 3, 1, 4}
201 << DataContainer{{1., 3., 4., 2., 5.}, {6., 8., 9., 7., 10.}, {11., 13., 14., 12., 15.}};
217 << Container{{1., 3., 4., 2., 5.}, {6., 8., 9., 7., 10.}, {11., 13., 14., 12., 15.}};
202 QTest::newRow("data2")
218 QTest::newRow("data2")
203 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
219 << flatten(
220 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
204 << std::vector<int>{2, 4, 3, 0, 1}
221 << std::vector<int>{2, 4, 3, 0, 1}
205 << DataContainer{{3., 5., 4., 1., 2.}, {8., 10., 9., 6., 7.}, {13., 15., 14., 11., 12.}};
222 << Container{{3., 5., 4., 1., 2.}, {8., 10., 9., 6., 7.}, {13., 15., 14., 11., 12.}};
206 }
223 }
207
224
208 void TestTwoDimArrayData::testSort()
225 void TestTwoDimArrayData::testSort()
209 {
226 {
210 QFETCH(DataContainer, inputData);
227 QFETCH(InputData, inputData);
211 QFETCH(std::vector<int>, sortPermutation);
228 QFETCH(std::vector<int>, sortPermutation);
212 QFETCH(DataContainer, expectedData);
229 QFETCH(Container, expectedData);
213
230
214 ArrayData<2> arrayData{inputData};
231 ArrayData<2> arrayData{inputData.first, inputData.second};
215 auto sortedArrayData = arrayData.sort(sortPermutation);
232 auto sortedArrayData = arrayData.sort(sortPermutation);
216 QVERIFY(sortedArrayData != nullptr);
233 QVERIFY(sortedArrayData != nullptr);
217
234
218 for (auto i = 0; i < expectedData.size(); ++i) {
235 verifyArrayData(*sortedArrayData, expectedData);
219 QVERIFY(sortedArrayData->data(i) == expectedData.at(i));
220 }
221 }
236 }
222
237
223 QTEST_MAIN(TestTwoDimArrayData)
238 QTEST_MAIN(TestTwoDimArrayData)
@@ -17,51 +17,6 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int sec
17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 }
18 }
19
19
20 /// Compares two vectors that can potentially contain NaN values
21 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
22 {
23 if (v1.size() != v2.size()) {
24 return false;
25 }
26
27 auto result = true;
28 auto v2It = v2.cbegin();
29 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
30 auto v1Value = *v1It;
31 auto v2Value = *v2It;
32
33 // If v1 is NaN, v2 has to be NaN too
34 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
35 }
36
37 return result;
38 }
39
40 bool compareVectors(const QVector<QVector<double> > &v1, const QVector<QVector<double> > &v2)
41 {
42 if (v1.size() != v2.size()) {
43 return false;
44 }
45
46 auto result = true;
47 for (auto i = 0; i < v1.size() && result; ++i) {
48 result &= compareVectors(v1.at(i), v2.at(i));
49 }
50
51 return result;
52 }
53
54 QVector<QVector<double> > valuesData(const ArrayData<1> &arrayData)
55 {
56 return QVector<QVector<double> >{arrayData.data()};
57 }
58
59 QVector<QVector<double> > valuesData(const ArrayData<2> &arrayData)
60 {
61 return arrayData.data();
62 }
63
64
65 QString inputFilePath(const QString &inputFileName)
20 QString inputFilePath(const QString &inputFileName)
66 {
21 {
67 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
22 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
@@ -106,10 +61,26 struct ExpectedResults {
106 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
61 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
107 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
62 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
108
63
109 // Checks values : as the vectors can potentially contain NaN values, we must use a
64 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
110 // custom vector comparison method
65 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
111 QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData));
66 expectedData.cend(),
112 QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData));
67 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
68 return equalFun(dataSeriesIt, expectedX);
69 }));
70 };
71
72 // Checks x-axis data
73 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
74 return seriesIt.x() == value;
75 });
76
77 // Checks values data of each component
78 for (auto i = 0; i < m_ValuesData.size(); ++i) {
79 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
80 auto itValue = seriesIt.value(i);
81 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
82 });
83 }
113 }
84 }
114 else {
85 else {
115 QVERIFY(results == nullptr);
86 QVERIFY(results == nullptr);
General Comments 0
You need to be logged in to leave comments. Login now