##// END OF EJS Templates
Adds methods min() and max() in ArrayData iterators...
Alexandre Leroux -
r607:9a52fd42b9d8
parent child
Show More
@@ -1,328 +1,348
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include <Common/SortUtils.h>
4 #include <Common/SortUtils.h>
5
5
6 #include <QReadLocker>
6 #include <QReadLocker>
7 #include <QReadWriteLock>
7 #include <QReadWriteLock>
8 #include <QVector>
8 #include <QVector>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 template <int Dim>
12 template <int Dim>
13 class ArrayData;
13 class ArrayData;
14
14
15 using DataContainer = QVector<QVector<double> >;
15 using DataContainer = QVector<QVector<double> >;
16
16
17 namespace arraydata_detail {
17 namespace arraydata_detail {
18
18
19 /// Struct used to sort ArrayData
19 /// Struct used to sort ArrayData
20 template <int Dim>
20 template <int Dim>
21 struct Sort {
21 struct Sort {
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
23 const std::vector<int> &sortPermutation)
23 const std::vector<int> &sortPermutation)
24 {
24 {
25 auto nbComponents = data.size();
25 auto nbComponents = data.size();
26 auto sortedData = DataContainer(nbComponents);
26 auto sortedData = DataContainer(nbComponents);
27
27
28 for (auto i = 0; i < nbComponents; ++i) {
28 for (auto i = 0; i < nbComponents; ++i) {
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
30 }
30 }
31
31
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
33 }
33 }
34 };
34 };
35
35
36 /// Specialization for uni-dimensional ArrayData
36 /// Specialization for uni-dimensional ArrayData
37 template <>
37 template <>
38 struct Sort<1> {
38 struct Sort<1> {
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
40 const std::vector<int> &sortPermutation)
40 const std::vector<int> &sortPermutation)
41 {
41 {
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
43 }
43 }
44 };
44 };
45
45
46 } // namespace arraydata_detail
46 } // namespace arraydata_detail
47
47
48 /**
48 /**
49 * @brief The ArrayData class represents a dataset for a data series.
49 * @brief The ArrayData class represents a dataset for a data series.
50 *
50 *
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
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
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
53 * number of values
53 * number of values
54 *
54 *
55 * @tparam Dim the dimension of the ArrayData (one or two)
55 * @tparam Dim the dimension of the ArrayData (one or two)
56 * @sa IDataSeries
56 * @sa IDataSeries
57 */
57 */
58 template <int Dim>
58 template <int Dim>
59 class ArrayData {
59 class ArrayData {
60 public:
60 public:
61 class IteratorValue {
61 class IteratorValue {
62 public:
62 public:
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
64 {
64 {
65 for (auto i = 0; i < container.size(); ++i) {
65 for (auto i = 0; i < container.size(); ++i) {
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
67 }
67 }
68 }
68 }
69
69
70 double at(int index) const { return *m_Its.at(index); }
70 double at(int index) const { return *m_Its.at(index); }
71 double first() const { return *m_Its.front(); }
71 double first() const { return *m_Its.front(); }
72
72
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 }
92
73 void next()
93 void next()
74 {
94 {
75 for (auto &it : m_Its) {
95 for (auto &it : m_Its) {
76 ++it;
96 ++it;
77 }
97 }
78 }
98 }
79
99
80 void prev()
100 void prev()
81 {
101 {
82 for (auto &it : m_Its) {
102 for (auto &it : m_Its) {
83 --it;
103 --it;
84 }
104 }
85 }
105 }
86
106
87 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
107 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
88
108
89 private:
109 private:
90 std::vector<DataContainer::value_type::const_iterator> m_Its;
110 std::vector<DataContainer::value_type::const_iterator> m_Its;
91 };
111 };
92
112
93 class Iterator {
113 class Iterator {
94 public:
114 public:
95 using iterator_category = std::forward_iterator_tag;
115 using iterator_category = std::forward_iterator_tag;
96 using value_type = const IteratorValue;
116 using value_type = const IteratorValue;
97 using difference_type = std::ptrdiff_t;
117 using difference_type = std::ptrdiff_t;
98 using pointer = value_type *;
118 using pointer = value_type *;
99 using reference = value_type &;
119 using reference = value_type &;
100
120
101 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
121 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
102
122
103 virtual ~Iterator() noexcept = default;
123 virtual ~Iterator() noexcept = default;
104 Iterator(const Iterator &) = default;
124 Iterator(const Iterator &) = default;
105 Iterator(Iterator &&) = default;
125 Iterator(Iterator &&) = default;
106 Iterator &operator=(const Iterator &) = default;
126 Iterator &operator=(const Iterator &) = default;
107 Iterator &operator=(Iterator &&) = default;
127 Iterator &operator=(Iterator &&) = default;
108
128
109 Iterator &operator++()
129 Iterator &operator++()
110 {
130 {
111 m_CurrentValue.next();
131 m_CurrentValue.next();
112 return *this;
132 return *this;
113 }
133 }
114
134
115 Iterator &operator--()
135 Iterator &operator--()
116 {
136 {
117 m_CurrentValue.prev();
137 m_CurrentValue.prev();
118 return *this;
138 return *this;
119 }
139 }
120
140
121 pointer operator->() const { return &m_CurrentValue; }
141 pointer operator->() const { return &m_CurrentValue; }
122 reference operator*() const { return m_CurrentValue; }
142 reference operator*() const { return m_CurrentValue; }
123
143
124 bool operator==(const Iterator &other) const
144 bool operator==(const Iterator &other) const
125 {
145 {
126 return m_CurrentValue == other.m_CurrentValue;
146 return m_CurrentValue == other.m_CurrentValue;
127 }
147 }
128
148
129 bool operator!=(const Iterator &other) const { return !(*this == other); }
149 bool operator!=(const Iterator &other) const { return !(*this == other); }
130
150
131 private:
151 private:
132 IteratorValue m_CurrentValue;
152 IteratorValue m_CurrentValue;
133 };
153 };
134
154
135 // ///// //
155 // ///// //
136 // Ctors //
156 // Ctors //
137 // ///// //
157 // ///// //
138
158
139 /**
159 /**
140 * Ctor for a unidimensional ArrayData
160 * Ctor for a unidimensional ArrayData
141 * @param data the data the ArrayData will hold
161 * @param data the data the ArrayData will hold
142 */
162 */
143 template <int D = Dim, typename = std::enable_if_t<D == 1> >
163 template <int D = Dim, typename = std::enable_if_t<D == 1> >
144 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
164 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
145 {
165 {
146 m_Data[0] = std::move(data);
166 m_Data[0] = std::move(data);
147 }
167 }
148
168
149 /**
169 /**
150 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
170 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
151 * greater than 2 and each component must have the same number of values
171 * greater than 2 and each component must have the same number of values
152 * @param data the data the ArrayData will hold
172 * @param data the data the ArrayData will hold
153 * @throws std::invalid_argument if the number of components is less than 2
173 * @throws std::invalid_argument if the number of components is less than 2
154 * @remarks if the number of values is not the same for each component, no value is set
174 * @remarks if the number of values is not the same for each component, no value is set
155 */
175 */
156 template <int D = Dim, typename = std::enable_if_t<D == 2> >
176 template <int D = Dim, typename = std::enable_if_t<D == 2> >
157 explicit ArrayData(DataContainer data)
177 explicit ArrayData(DataContainer data)
158 {
178 {
159 auto nbComponents = data.size();
179 auto nbComponents = data.size();
160 if (nbComponents < 2) {
180 if (nbComponents < 2) {
161 throw std::invalid_argument{
181 throw std::invalid_argument{
162 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
182 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
163 .arg(data.size())
183 .arg(data.size())
164 .toStdString()};
184 .toStdString()};
165 }
185 }
166
186
167 auto nbValues = data.front().size();
187 auto nbValues = data.front().size();
168 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
188 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
169 return component.size() == nbValues;
189 return component.size() == nbValues;
170 })) {
190 })) {
171 m_Data = std::move(data);
191 m_Data = std::move(data);
172 }
192 }
173 else {
193 else {
174 m_Data = DataContainer{nbComponents, QVector<double>{}};
194 m_Data = DataContainer{nbComponents, QVector<double>{}};
175 }
195 }
176 }
196 }
177
197
178 /// Copy ctor
198 /// Copy ctor
179 explicit ArrayData(const ArrayData &other)
199 explicit ArrayData(const ArrayData &other)
180 {
200 {
181 QReadLocker otherLocker{&other.m_Lock};
201 QReadLocker otherLocker{&other.m_Lock};
182 m_Data = other.m_Data;
202 m_Data = other.m_Data;
183 }
203 }
184
204
185 // /////////////// //
205 // /////////////// //
186 // General methods //
206 // General methods //
187 // /////////////// //
207 // /////////////// //
188
208
189 /**
209 /**
190 * Merges into the array data an other array data. The two array datas must have the same number
210 * Merges into the array data an other array data. The two array datas must have the same number
191 * of components so the merge can be done
211 * of components so the merge can be done
192 * @param other the array data to merge with
212 * @param other the array data to merge with
193 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
213 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
194 * inserted at the end
214 * inserted at the end
195 */
215 */
196 void add(const ArrayData<Dim> &other, bool prepend = false)
216 void add(const ArrayData<Dim> &other, bool prepend = false)
197 {
217 {
198 QWriteLocker locker{&m_Lock};
218 QWriteLocker locker{&m_Lock};
199 QReadLocker otherLocker{&other.m_Lock};
219 QReadLocker otherLocker{&other.m_Lock};
200
220
201 auto nbComponents = m_Data.size();
221 auto nbComponents = m_Data.size();
202 if (nbComponents != other.m_Data.size()) {
222 if (nbComponents != other.m_Data.size()) {
203 return;
223 return;
204 }
224 }
205
225
206 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
226 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
207 if (prepend) {
227 if (prepend) {
208 const auto &otherData = other.data(componentIndex);
228 const auto &otherData = other.data(componentIndex);
209 const auto otherDataSize = otherData.size();
229 const auto otherDataSize = otherData.size();
210
230
211 auto &data = m_Data[componentIndex];
231 auto &data = m_Data[componentIndex];
212 data.insert(data.begin(), otherDataSize, 0.);
232 data.insert(data.begin(), otherDataSize, 0.);
213
233
214 for (auto i = 0; i < otherDataSize; ++i) {
234 for (auto i = 0; i < otherDataSize; ++i) {
215 data.replace(i, otherData.at(i));
235 data.replace(i, otherData.at(i));
216 }
236 }
217 }
237 }
218 else {
238 else {
219 m_Data[componentIndex] += other.data(componentIndex);
239 m_Data[componentIndex] += other.data(componentIndex);
220 }
240 }
221 }
241 }
222 }
242 }
223
243
224 void clear()
244 void clear()
225 {
245 {
226 QWriteLocker locker{&m_Lock};
246 QWriteLocker locker{&m_Lock};
227
247
228 auto nbComponents = m_Data.size();
248 auto nbComponents = m_Data.size();
229 for (auto i = 0; i < nbComponents; ++i) {
249 for (auto i = 0; i < nbComponents; ++i) {
230 m_Data[i].clear();
250 m_Data[i].clear();
231 }
251 }
232 }
252 }
233
253
234 int componentCount() const noexcept { return m_Data.size(); }
254 int componentCount() const noexcept { return m_Data.size(); }
235
255
236 /**
256 /**
237 * @return the data of a component
257 * @return the data of a component
238 * @param componentIndex the index of the component to retrieve the data
258 * @param componentIndex the index of the component to retrieve the data
239 * @return the component's data, empty vector if the index is invalid
259 * @return the component's data, empty vector if the index is invalid
240 */
260 */
241 QVector<double> data(int componentIndex) const noexcept
261 QVector<double> data(int componentIndex) const noexcept
242 {
262 {
243 QReadLocker locker{&m_Lock};
263 QReadLocker locker{&m_Lock};
244
264
245 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
265 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
246 : QVector<double>{};
266 : QVector<double>{};
247 }
267 }
248
268
249 /// @return the size (i.e. number of values) of a single component
269 /// @return the size (i.e. number of values) of a single component
250 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
270 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
251 int size() const
271 int size() const
252 {
272 {
253 QReadLocker locker{&m_Lock};
273 QReadLocker locker{&m_Lock};
254 return m_Data[0].size();
274 return m_Data[0].size();
255 }
275 }
256
276
257 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
277 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
258 {
278 {
259 QReadLocker locker{&m_Lock};
279 QReadLocker locker{&m_Lock};
260 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
280 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
261 }
281 }
262
282
263 // ///////// //
283 // ///////// //
264 // Iterators //
284 // Iterators //
265 // ///////// //
285 // ///////// //
266
286
267 Iterator cbegin() const { return Iterator{m_Data, true}; }
287 Iterator cbegin() const { return Iterator{m_Data, true}; }
268 Iterator cend() const { return Iterator{m_Data, false}; }
288 Iterator cend() const { return Iterator{m_Data, false}; }
269
289
270 // ///////////// //
290 // ///////////// //
271 // 1-dim methods //
291 // 1-dim methods //
272 // ///////////// //
292 // ///////////// //
273
293
274 /**
294 /**
275 * @return the data at a specified index
295 * @return the data at a specified index
276 * @remarks index must be a valid position
296 * @remarks index must be a valid position
277 * @remarks this method is only available for a unidimensional ArrayData
297 * @remarks this method is only available for a unidimensional ArrayData
278 */
298 */
279 template <int D = Dim, typename = std::enable_if_t<D == 1> >
299 template <int D = Dim, typename = std::enable_if_t<D == 1> >
280 double at(int index) const noexcept
300 double at(int index) const noexcept
281 {
301 {
282 QReadLocker locker{&m_Lock};
302 QReadLocker locker{&m_Lock};
283 return m_Data[0].at(index);
303 return m_Data[0].at(index);
284 }
304 }
285
305
286 /**
306 /**
287 * @return the data as a vector, as a const reference
307 * @return the data as a vector, as a const reference
288 * @remarks this method is only available for a unidimensional ArrayData
308 * @remarks this method is only available for a unidimensional ArrayData
289 */
309 */
290 template <int D = Dim, typename = std::enable_if_t<D == 1> >
310 template <int D = Dim, typename = std::enable_if_t<D == 1> >
291 const QVector<double> &cdata() const noexcept
311 const QVector<double> &cdata() const noexcept
292 {
312 {
293 QReadLocker locker{&m_Lock};
313 QReadLocker locker{&m_Lock};
294 return m_Data.at(0);
314 return m_Data.at(0);
295 }
315 }
296
316
297 /**
317 /**
298 * @return the data as a vector
318 * @return the data as a vector
299 * @remarks this method is only available for a unidimensional ArrayData
319 * @remarks this method is only available for a unidimensional ArrayData
300 */
320 */
301 template <int D = Dim, typename = std::enable_if_t<D == 1> >
321 template <int D = Dim, typename = std::enable_if_t<D == 1> >
302 QVector<double> data() const noexcept
322 QVector<double> data() const noexcept
303 {
323 {
304 QReadLocker locker{&m_Lock};
324 QReadLocker locker{&m_Lock};
305 return m_Data[0];
325 return m_Data[0];
306 }
326 }
307
327
308 // ///////////// //
328 // ///////////// //
309 // 2-dim methods //
329 // 2-dim methods //
310 // ///////////// //
330 // ///////////// //
311
331
312 /**
332 /**
313 * @return the data
333 * @return the data
314 * @remarks this method is only available for a two-dimensional ArrayData
334 * @remarks this method is only available for a two-dimensional ArrayData
315 */
335 */
316 template <int D = Dim, typename = std::enable_if_t<D == 2> >
336 template <int D = Dim, typename = std::enable_if_t<D == 2> >
317 DataContainer data() const noexcept
337 DataContainer data() const noexcept
318 {
338 {
319 QReadLocker locker{&m_Lock};
339 QReadLocker locker{&m_Lock};
320 return m_Data;
340 return m_Data;
321 }
341 }
322
342
323 private:
343 private:
324 DataContainer m_Data;
344 DataContainer m_Data;
325 mutable QReadWriteLock m_Lock;
345 mutable QReadWriteLock m_Lock;
326 };
346 };
327
347
328 #endif // SCIQLOP_ARRAYDATA_H
348 #endif // SCIQLOP_ARRAYDATA_H
General Comments 0
You need to be logged in to leave comments. Login now