##// 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 57 * @param container the container sorted
42 58 * @param sortPermutation the indices used to sort the container
43 59 * @return the container sorted
@@ -45,18 +61,24 struct SortUtils {
45 61 * indices and its range is [0 ; vector.size()[ )
46 62 */
47 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 70 return Container{};
52 71 }
53 72
54 73 // Inits result
55 74 auto sortedData = Container{};
56 sortedData.resize(container.size());
75 sortedData.reserve(containerSize);
57 76
58 std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(),
59 [&container](int i) { return container.at(i); });
77 for (auto i = 0, componentIndex = 0, permutationIndex = 0; i < containerSize;
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 83 return sortedData;
62 84 }
@@ -1,6 +1,7
1 1 #ifndef SCIQLOP_ARRAYDATA_H
2 2 #define SCIQLOP_ARRAYDATA_H
3 3
4 #include "Data/ArrayDataIterator.h"
4 5 #include <Common/SortUtils.h>
5 6
6 7 #include <QReadLocker>
@@ -12,146 +13,110
12 13 template <int Dim>
13 14 class ArrayData;
14 15
15 using DataContainer = QVector<QVector<double> >;
16 using DataContainer = QVector<double>;
16 17
17 18 namespace arraydata_detail {
18 19
19 20 /// Struct used to sort ArrayData
20 21 template <int Dim>
21 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 24 const std::vector<int> &sortPermutation)
24 25 {
25 auto nbComponents = data.size();
26 auto sortedData = DataContainer(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));
26 return std::make_shared<ArrayData<Dim> >(
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
33 28 }
34 29 };
35 30
36 31 /// Specialization for uni-dimensional ArrayData
37 32 template <>
38 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 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 42 template <int Dim>
59 class ArrayData {
60 public:
61 class IteratorValue {
43 class IteratorValue : public ArrayDataIteratorValue::Impl {
62 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
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 }
50 IteratorValue(const IteratorValue &other) = default;
92 51
93 void next()
52 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
94 53 {
95 for (auto &it : m_Its) {
96 ++it;
97 }
54 return std::make_unique<IteratorValue<Dim> >(*this);
98 55 }
99 56
100 void prev()
101 {
102 for (auto &it : m_Its) {
103 --it;
57 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
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; }
108
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 &;
65 void next() override { std::advance(m_It, m_NbComponents); }
66 void prev() override { std::advance(m_It, -m_NbComponents); }
120 67
121 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
122
123 virtual ~Iterator() noexcept = default;
124 Iterator(const Iterator &) = default;
125 Iterator(Iterator &&) = default;
126 Iterator &operator=(const Iterator &) = default;
127 Iterator &operator=(Iterator &&) = default;
128
129 Iterator &operator++()
68 double at(int componentIndex) const override { return *(m_It + componentIndex); }
69 double first() const override { return *m_It; }
70 double min() const override
130 71 {
131 m_CurrentValue.next();
132 return *this;
133 }
72 auto values = this->values();
73 auto end = values.cend();
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();
138 return *this;
82 auto values = this->values();
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; }
142 reference operator*() const { return m_CurrentValue; }
143
144 bool operator==(const Iterator &other) const
90 private:
91 std::vector<double> values() 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:
152 IteratorValue m_CurrentValue;
101 DataContainer::const_iterator m_It;
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 121 // Ctors //
157 122 // ///// //
@@ -161,37 +126,34 public:
161 126 * @param data the data the ArrayData will hold
162 127 */
163 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
171 * greater than 2 and each component must have the same number of values
134 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
135 * greater than 2 and must be a divisor of the total number of data in the vector
172 136 * @param data the data the ArrayData will hold
173 * @throws std::invalid_argument if the number of components is less than 2
174 * @remarks if the number of values is not the same for each component, no value is set
137 * @param nbComponents the number of components
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 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 145 if (nbComponents < 2) {
181 146 throw std::invalid_argument{
182 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
183 .arg(data.size())
147 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
148 .arg(nbComponents)
184 149 .toStdString()};
185 150 }
186 151
187 auto nbValues = data.front().size();
188 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
189 return component.size() == nbValues;
190 })) {
191 m_Data = std::move(data);
192 }
193 else {
194 m_Data = DataContainer{nbComponents, QVector<double>{}};
152 if (m_Data.size() % m_NbComponents != 0) {
153 throw std::invalid_argument{QString{
154 "The number of components (%1) is inconsistent with the total number of data (%2)"}
155 .arg(m_Data.size(), nbComponents)
156 .toStdString()};
195 157 }
196 158 }
197 159
@@ -200,6 +162,7 public:
200 162 {
201 163 QReadLocker otherLocker{&other.m_Lock};
202 164 m_Data = other.m_Data;
165 m_NbComponents = other.m_NbComponents;
203 166 }
204 167
205 168 // /////////////// //
@@ -218,91 +181,75 public:
218 181 QWriteLocker locker{&m_Lock};
219 182 QReadLocker otherLocker{&other.m_Lock};
220 183
221 auto nbComponents = m_Data.size();
222 if (nbComponents != other.m_Data.size()) {
184 if (m_NbComponents != other.componentCount()) {
223 185 return;
224 186 }
225 187
226 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
227 188 if (prepend) {
228 const auto &otherData = other.data(componentIndex);
229 const auto otherDataSize = otherData.size();
230
231 auto &data = m_Data[componentIndex];
232 data.insert(data.begin(), otherDataSize, 0.);
233
189 auto otherDataSize = other.m_Data.size();
190 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
234 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 195 else {
239 m_Data[componentIndex] += other.data(componentIndex);
240 }
196 m_Data.append(other.m_Data);
241 197 }
242 198 }
243 199
244 200 void clear()
245 201 {
246 202 QWriteLocker locker{&m_Lock};
247
248 auto nbComponents = m_Data.size();
249 for (auto i = 0; i < nbComponents; ++i) {
250 m_Data[i].clear();
251 }
203 m_Data.clear();
252 204 }
253 205
254 int componentCount() const noexcept { return m_Data.size(); }
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 }
206 int componentCount() const noexcept { return m_NbComponents; }
268 207
269 208 /// @return the size (i.e. number of values) of a single component
270 209 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
271 210 int size() const
272 211 {
273 212 QReadLocker locker{&m_Lock};
274 return m_Data[0].size();
213 return m_Data.size() / m_NbComponents;
275 214 }
276 215
277 216 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
278 217 {
279 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 223 // Iterators //
285 224 // ///////// //
286 225
287 Iterator cbegin() const { return Iterator{m_Data, true}; }
288 Iterator cend() const { return Iterator{m_Data, false}; }
226 ArrayDataIterator cbegin() const
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 240 * @return the data at a specified index
296 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 243 double at(int index) const noexcept
301 244 {
302 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 254 * @return the data as a vector, as a const reference
308 255 * @remarks this method is only available for a unidimensional ArrayData
@@ -311,37 +258,13 public:
311 258 const QVector<double> &cdata() const noexcept
312 259 {
313 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 261 return m_Data;
341 262 }
342 263
343 264 private:
344 265 DataContainer m_Data;
266 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
267 int m_NbComponents;
345 268 mutable QReadWriteLock m_Lock;
346 269 };
347 270
@@ -69,8 +69,8 public:
69 69 double maxValue() const override { return m_ValuesIt->max(); }
70 70
71 71 private:
72 ArrayData<1>::Iterator m_XIt;
73 typename ArrayData<Dim>::Iterator m_ValuesIt;
72 ArrayDataIterator m_XIt;
73 ArrayDataIterator m_ValuesIt;
74 74 };
75 75 } // namespace dataseries_detail
76 76
@@ -2,6 +2,7
2 2 #define SCIQLOP_DATASERIESITERATOR_H
3 3
4 4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
5 6
6 7 #include <memory>
7 8
@@ -54,35 +55,6 private:
54 55 std::unique_ptr<Impl> m_Impl;
55 56 };
56 57
57 /**
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 };
58 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
87 59
88 60 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -11,8 +11,8
11 11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
12 12 public:
13 13 /**
14 * Ctor. The vectors must have the same size, otherwise a ScalarSeries with no values will be
15 * created.
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
15 * ScalarSeries with no values will be created.
16 16 * @param xAxisData x-axis data
17 17 * @param xvaluesData x-values data
18 18 * @param yvaluesData y-values data
@@ -22,6 +22,10 public:
22 22 QVector<double> yValuesData, QVector<double> zValuesData,
23 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 29 std::unique_ptr<IDataSeries> clone() const;
26 30
27 31 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
@@ -56,30 +56,3 double DataSeriesIteratorValue::maxValue() const
56 56 {
57 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 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 41 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
4 42 QVector<double> yValuesData, QVector<double> zValuesData,
5 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 52 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
7 std::make_shared<ArrayData<2> >(QVector<QVector<double> >{
8 std::move(xValuesData), std::move(yValuesData), std::move(zValuesData)}),
9 valuesUnit}
53 std::make_shared<ArrayData<2> >(std::move(valuesData), 3), valuesUnit}
10 54 {
11 55 }
12 56
@@ -10,6 +10,20
10 10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 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 27 class TestDataSeries : public QObject {
14 28 Q_OBJECT
15 29 private:
@@ -164,13 +178,7 void TestDataSeries::testCtor()
164 178 QFETCH(QVector<double>, expectedXAxisData);
165 179 QFETCH(QVector<double>, expectedValuesData);
166 180
167 auto seriesXAxisData = series->xAxisData()->data();
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()));
181 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
174 182 }
175 183
176 184 namespace {
@@ -250,13 +258,7 void TestDataSeries::testMerge()
250 258 QFETCH(QVector<double>, expectedXAxisData);
251 259 QFETCH(QVector<double>, expectedValuesData);
252 260
253 auto seriesXAxisData = dataSeries->xAxisData()->data();
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()));
261 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
260 262 }
261 263
262 264 void TestDataSeries::testMinXAxisData_data()
@@ -438,12 +440,7 void TestDataSeries::testXAxisRange()
438 440 QFETCH(QVector<double>, expectedValuesData);
439 441
440 442 auto bounds = dataSeries->xAxisRange(min, max);
441 QVERIFY(std::equal(bounds.first, bounds.second, expectedXAxisData.cbegin(),
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; }));
443 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
447 444 }
448 445
449 446 void TestDataSeries::testValuesBoundsScalar_data()
@@ -2,6 +2,17
2 2 #include <QObject>
3 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 16 class TestOneDimArrayData : public QObject {
6 17 Q_OBJECT
7 18 private slots:
@@ -9,10 +20,6 private slots:
9 20 void testData_data();
10 21 void testData();
11 22
12 /// Tests @sa ArrayData::data(int componentIndex)
13 void testDataByComponentIndex_data();
14 void testDataByComponentIndex();
15
16 23 /// Tests @sa ArrayData::add()
17 24 void testAdd_data();
18 25 void testAdd();
@@ -51,32 +58,7 void TestOneDimArrayData::testData()
51 58 QFETCH(QVector<double>, expectedData);
52 59
53 60 ArrayData<1> arrayData{inputData};
54 QVERIFY(arrayData.data() == 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);
61 verifyArrayData(arrayData, expectedData);
80 62 }
81 63
82 64 void TestOneDimArrayData::testAdd_data()
@@ -107,7 +89,7 void TestOneDimArrayData::testAdd()
107 89 ArrayData<1> other{otherData};
108 90
109 91 arrayData.add(other, prepend);
110 QVERIFY(arrayData.data() == expectedData);
92 verifyArrayData(arrayData, expectedData);
111 93 }
112 94
113 95 void TestOneDimArrayData::testAt_data()
@@ -147,7 +129,7 void TestOneDimArrayData::testClear()
147 129
148 130 ArrayData<1> arrayData{inputData};
149 131 arrayData.clear();
150 QVERIFY(arrayData.data() == QVector<double>{});
132 verifyArrayData(arrayData, QVector<double>{});
151 133 }
152 134
153 135 void TestOneDimArrayData::testSize_data()
@@ -192,7 +174,7 void TestOneDimArrayData::testSort()
192 174 ArrayData<1> arrayData{inputData};
193 175 auto sortedArrayData = arrayData.sort(sortPermutation);
194 176 QVERIFY(sortedArrayData != nullptr);
195 QVERIFY(sortedArrayData->data() == expectedData);
177 verifyArrayData(*sortedArrayData, expectedData);
196 178 }
197 179
198 180 QTEST_MAIN(TestOneDimArrayData)
@@ -2,15 +2,55
2 2 #include <QObject>
3 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 51 class TestTwoDimArrayData : public QObject {
8 52 Q_OBJECT
9 53 private slots:
10 /// Tests @sa ArrayData::data(int componentIndex)
11 void testDataByComponentIndex_data();
12 void testDataByComponentIndex();
13
14 54 /// Tests @sa ArrayData ctor
15 55 void testCtor_data();
16 56 void testCtor();
@@ -32,192 +72,167 private slots:
32 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 75 void TestTwoDimArrayData::testCtor_data()
64 76 {
65 77 // Test structure
66 QTest::addColumn<DataContainer>("inputData"); // array data's input
78 QTest::addColumn<InputData>("inputData"); // array data's input
67 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 82 // Test cases
71 QTest::newRow("validInput")
72 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
73 << true
74 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
75 QTest::newRow("malformedInput (components of the array data haven't the same size")
76 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8.}, {11., 12.}} << true
77 << DataContainer{{}, {}, {}};
78 QTest::newRow("invalidInput (less than tow components") << DataContainer{{1., 2., 3., 4., 5.}}
79 << false << DataContainer{{}, {}, {}};
83 QTest::newRow("validInput") << flatten(Container{{1., 2., 3., 4., 5.},
84 {6., 7., 8., 9., 10.},
85 {11., 12., 13., 14., 15.}})
86 << true << Container{{1., 2., 3., 4., 5.},
87 {6., 7., 8., 9., 10.},
88 {11., 12., 13., 14., 15.}};
89 QTest::newRow("invalidInput (invalid data size")
90 << InputData{{1., 2., 3., 4., 5., 6., 7.}, 3} << false << Container{{}, {}, {}};
91 QTest::newRow("invalidInput (less than two components")
92 << flatten(Container{{1., 2., 3., 4., 5.}}) << false << Container{{}, {}, {}};
80 93 }
81 94
82 95 void TestTwoDimArrayData::testCtor()
83 96 {
84 QFETCH(DataContainer, inputData);
97 QFETCH(InputData, inputData);
85 98 QFETCH(bool, success);
86 99
87 100 if (success) {
88 QFETCH(DataContainer, expectedData);
89
90 ArrayData<2> arrayData{inputData};
101 QFETCH(Container, expectedData);
91 102
92 for (auto i = 0; i < expectedData.size(); ++i) {
93 QVERIFY(arrayData.data(i) == expectedData.at(i));
94 }
103 ArrayData<2> arrayData{inputData.first, inputData.second};
104 verifyArrayData(arrayData, expectedData);
95 105 }
96 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 112 void TestTwoDimArrayData::testAdd_data()
102 113 {
103 114 // Test structure
104 QTest::addColumn<DataContainer>("inputData"); // array's data input
105 QTest::addColumn<DataContainer>("otherData"); // array data's input to merge with
115 QTest::addColumn<InputData>("inputData"); // array's data input
116 QTest::addColumn<InputData>("otherData"); // array data's input to merge with
106 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 120 // Test cases
110 auto inputData
111 = DataContainer{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}};
121 auto inputData = flatten(
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}};
114 auto tensorContainer = DataContainer{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28},
115 {36., 37., 38.}, {46., 47., 48.}, {56., 57., 58}};
124 auto vectorContainer = flatten(Container{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}});
125 auto tensorContainer = flatten(Container{{6., 7., 8.},
126 {16., 17., 18.},
127 {26., 27., 28},
128 {36., 37., 38.},
129 {46., 47., 48.},
130 {56., 57., 58}});
116 131
117 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 134 {11., 12., 13., 14., 15., 16., 17., 18.},
120 135 {21., 22., 23., 24., 25., 26., 27., 28}};
121 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 138 {16., 17., 18., 11., 12., 13., 14., 15.},
124 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 146 void TestTwoDimArrayData::testAdd()
129 147 {
130 QFETCH(DataContainer, inputData);
131 QFETCH(DataContainer, otherData);
148 QFETCH(InputData, inputData);
149 QFETCH(InputData, otherData);
132 150 QFETCH(bool, prepend);
133 QFETCH(DataContainer, expectedData);
151 QFETCH(Container, expectedData);
134 152
135 ArrayData<2> arrayData{inputData};
136 ArrayData<2> other{otherData};
153 ArrayData<2> arrayData{inputData.first, inputData.second};
154 ArrayData<2> other{otherData.first, otherData.second};
137 155
138 156 arrayData.add(other, prepend);
139 157
140 for (auto i = 0; i < expectedData.size(); ++i) {
141 QVERIFY(arrayData.data(i) == expectedData.at(i));
142 }
158 verifyArrayData(arrayData, expectedData);
143 159 }
144 160
145 161 void TestTwoDimArrayData::testClear_data()
146 162 {
147 163 // Test structure
148 QTest::addColumn<DataContainer>("inputData"); // array data's input
164 QTest::addColumn<InputData>("inputData"); // array data's input
149 165
150 166 // Test cases
151 QTest::newRow("data1") << DataContainer{
152 {1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
167 QTest::newRow("data1") << flatten(
168 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}});
153 169 }
154 170
155 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 176 arrayData.clear();
161 177
162 for (auto i = 0; i < inputData.size(); ++i) {
163 QVERIFY(arrayData.data(i) == QVector<double>{});
164 }
178 auto emptyData = Container(inputData.second, QVector<double>{});
179 verifyArrayData(arrayData, emptyData);
165 180 }
166 181
167 182 void TestTwoDimArrayData::testSize_data()
168 183 {
169 184 // Test structure
170 QTest::addColumn<QVector<QVector<double> > >("inputData"); // array data's input
185 QTest::addColumn<InputData>("inputData"); // array data's input
171 186 QTest::addColumn<int>("expectedSize"); // expected array data size
172 187
173 188 // Test cases
174 QTest::newRow("data1") << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}} << 5;
175 QTest::newRow("data2") << DataContainer{{1., 2., 3., 4., 5.},
189 QTest::newRow("data1") << flatten(Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}}) << 5;
190 QTest::newRow("data2") << flatten(Container{{1., 2., 3., 4., 5.},
176 191 {6., 7., 8., 9., 10.},
177 {11., 12., 13., 14., 15.}}
192 {11., 12., 13., 14., 15.}})
178 193 << 5;
179 194 }
180 195
181 196 void TestTwoDimArrayData::testSize()
182 197 {
183 QFETCH(DataContainer, inputData);
198 QFETCH(InputData, inputData);
184 199 QFETCH(int, expectedSize);
185 200
186 ArrayData<2> arrayData{inputData};
201 ArrayData<2> arrayData{inputData.first, inputData.second};
187 202 QVERIFY(arrayData.size() == expectedSize);
188 203 }
189 204
190 205 void TestTwoDimArrayData::testSort_data()
191 206 {
192 207 // Test structure
193 QTest::addColumn<DataContainer>("inputData"); // array data's input
208 QTest::addColumn<InputData>("inputData"); // array data's input
194 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 212 // Test cases
198 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 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 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 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 225 void TestTwoDimArrayData::testSort()
209 226 {
210 QFETCH(DataContainer, inputData);
227 QFETCH(InputData, inputData);
211 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 232 auto sortedArrayData = arrayData.sort(sortPermutation);
216 233 QVERIFY(sortedArrayData != nullptr);
217 234
218 for (auto i = 0; i < expectedData.size(); ++i) {
219 QVERIFY(sortedArrayData->data(i) == expectedData.at(i));
220 }
235 verifyArrayData(*sortedArrayData, expectedData);
221 236 }
222 237
223 238 QTEST_MAIN(TestTwoDimArrayData)
@@ -17,51 +17,6 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int sec
17 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 20 QString inputFilePath(const QString &inputFileName)
66 21 {
67 22 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
@@ -106,10 +61,26 struct ExpectedResults {
106 61 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
107 62 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
108 63
109 // Checks values : as the vectors can potentially contain NaN values, we must use a
110 // custom vector comparison method
111 QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData));
112 QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData));
64 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
65 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
66 expectedData.cend(),
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 85 else {
115 86 QVERIFY(results == nullptr);
General Comments 0
You need to be logged in to leave comments. Login now