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