##// END OF EJS Templates
Improves random access iterator performance
Alexandre Leroux -
r647:710e631137a0 feature/ImprovePe...
parent child
Show More
@@ -1,369 +1,367
1 1 #ifndef SCIQLOP_ARRAYDATA_H
2 2 #define SCIQLOP_ARRAYDATA_H
3 3
4 4 #include "Data/ArrayDataIterator.h"
5 5 #include <Common/SortUtils.h>
6 6
7 7 #include <QReadLocker>
8 8 #include <QReadWriteLock>
9 9 #include <QVector>
10 10
11 11 #include <memory>
12 12
13 13 template <int Dim>
14 14 class ArrayData;
15 15
16 16 using DataContainer = std::vector<double>;
17 17
18 18 namespace arraydata_detail {
19 19
20 20 /// Struct used to sort ArrayData
21 21 template <int Dim>
22 22 struct Sort {
23 23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
24 24 const std::vector<int> &sortPermutation)
25 25 {
26 26 return std::make_shared<ArrayData<Dim> >(
27 27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
28 28 }
29 29 };
30 30
31 31 /// Specialization for uni-dimensional ArrayData
32 32 template <>
33 33 struct Sort<1> {
34 34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
35 35 const std::vector<int> &sortPermutation)
36 36 {
37 37 Q_UNUSED(nbComponents)
38 38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
39 39 }
40 40 };
41 41
42 42 template <int Dim, bool IsConst>
43 43 class IteratorValue;
44 44
45 45 template <int Dim, bool IsConst>
46 46 struct IteratorValueBuilder {
47 47 };
48 48
49 49 template <int Dim>
50 50 struct IteratorValueBuilder<Dim, true> {
51 51 using DataContainerIterator = DataContainer::const_iterator;
52 52
53 53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
54 54 };
55 55
56 56 template <int Dim>
57 57 struct IteratorValueBuilder<Dim, false> {
58 58 using DataContainerIterator = DataContainer::iterator;
59 59
60 60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
61 61 {
62 62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
63 63 std::iter_swap(o1.m_It + i, o2.m_It + i);
64 64 }
65 65 }
66 66 };
67 67
68 68 template <int Dim, bool IsConst>
69 69 class IteratorValue : public ArrayDataIteratorValue::Impl {
70 70 public:
71 71 friend class ArrayData<Dim>;
72 72 friend class IteratorValueBuilder<Dim, IsConst>;
73 73
74 74 using DataContainerIterator =
75 75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
76 76
77 77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
78 78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
79 79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
80 80 {
81 81 }
82 82
83 83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
84 84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
85 85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
86 86 {
87 87 }
88 88
89 89 IteratorValue(const IteratorValue &other) = default;
90 90
91 91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
92 92 {
93 93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
94 94 }
95 95
96 96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
97 97 /// @todo ALX : validate
98 98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
99 99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
100 100 }
101 101 catch (const std::bad_cast &) {
102 102 return 0;
103 103 }
104 104
105 105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
106 106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
107 107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
108 108 }
109 109 catch (const std::bad_cast &) {
110 110 return false;
111 111 }
112 112
113 113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
114 114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
115 115 return m_It < otherImpl.m_It;
116 116 }
117 117 catch (const std::bad_cast &) {
118 118 return false;
119 119 }
120 120
121 121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
122 122 {
123 123 auto result = clone();
124 while (offset--) {
125 result->next();
126 }
124 result->next(offset);
127 125 return result;
128 126 }
129 127
130 void next() override { std::advance(m_It, m_NbComponents); }
128 void next(int offset) override { std::advance(m_It, offset * m_NbComponents); }
131 129 void prev() override { std::advance(m_It, -m_NbComponents); }
132 130
133 131 double at(int componentIndex) const override { return *(m_It + componentIndex); }
134 132 double first() const override { return *m_It; }
135 133 double min() const override
136 134 {
137 135 auto values = this->values();
138 136 auto end = values.cend();
139 137 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
140 138 return SortUtils::minCompareWithNaN(v1, v2);
141 139 });
142 140
143 141 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
144 142 }
145 143 double max() const override
146 144 {
147 145 auto values = this->values();
148 146 auto end = values.cend();
149 147 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
150 148 return SortUtils::maxCompareWithNaN(v1, v2);
151 149 });
152 150 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
153 151 }
154 152
155 153 QVector<double> values() const override
156 154 {
157 155 auto result = QVector<double>{};
158 156 for (auto i = 0; i < m_NbComponents; ++i) {
159 157 result.push_back(*(m_It + i));
160 158 }
161 159
162 160 return result;
163 161 }
164 162
165 163 void swap(ArrayDataIteratorValue::Impl &other) override
166 164 {
167 165 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
168 166 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
169 167 }
170 168
171 169 private:
172 170 DataContainerIterator m_It;
173 171 int m_NbComponents;
174 172 };
175 173
176 174 } // namespace arraydata_detail
177 175
178 176 /**
179 177 * @brief The ArrayData class represents a dataset for a data series.
180 178 *
181 179 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
182 180 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
183 181 * number of values
184 182 *
185 183 * @tparam Dim the dimension of the ArrayData (one or two)
186 184 * @sa IDataSeries
187 185 */
188 186 template <int Dim>
189 187 class ArrayData {
190 188 public:
191 189 // ///// //
192 190 // Ctors //
193 191 // ///// //
194 192
195 193 /**
196 194 * Ctor for a unidimensional ArrayData
197 195 * @param data the data the ArrayData will hold
198 196 */
199 197 template <int D = Dim, typename = std::enable_if_t<D == 1> >
200 198 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
201 199 {
202 200 }
203 201
204 202 /**
205 203 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
206 204 * greater than 2 and must be a divisor of the total number of data in the vector
207 205 * @param data the data the ArrayData will hold
208 206 * @param nbComponents the number of components
209 207 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
210 208 * of the size of the data
211 209 */
212 210 template <int D = Dim, typename = std::enable_if_t<D == 2> >
213 211 explicit ArrayData(DataContainer data, int nbComponents)
214 212 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
215 213 {
216 214 if (nbComponents < 2) {
217 215 throw std::invalid_argument{
218 216 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
219 217 .arg(nbComponents)
220 218 .toStdString()};
221 219 }
222 220
223 221 if (m_Data.size() % m_NbComponents != 0) {
224 222 throw std::invalid_argument{QString{
225 223 "The number of components (%1) is inconsistent with the total number of data (%2)"}
226 224 .arg(m_Data.size(), nbComponents)
227 225 .toStdString()};
228 226 }
229 227 }
230 228
231 229 /// Copy ctor
232 230 explicit ArrayData(const ArrayData &other)
233 231 {
234 232 QReadLocker otherLocker{&other.m_Lock};
235 233 m_Data = other.m_Data;
236 234 m_NbComponents = other.m_NbComponents;
237 235 }
238 236
239 237 // /////////////// //
240 238 // General methods //
241 239 // /////////////// //
242 240
243 241 /**
244 242 * Merges into the array data an other array data. The two array datas must have the same number
245 243 * of components so the merge can be done
246 244 * @param other the array data to merge with
247 245 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
248 246 * inserted at the end
249 247 */
250 248 void add(const ArrayData<Dim> &other, bool prepend = false)
251 249 {
252 250 QWriteLocker locker{&m_Lock};
253 251 QReadLocker otherLocker{&other.m_Lock};
254 252
255 253 if (m_NbComponents != other.componentCount()) {
256 254 return;
257 255 }
258 256
259 257 insert(other.cbegin(), other.cend(), prepend);
260 258 }
261 259
262 260 void clear()
263 261 {
264 262 QWriteLocker locker{&m_Lock};
265 263 m_Data.clear();
266 264 }
267 265
268 266 int componentCount() const noexcept { return m_NbComponents; }
269 267
270 268 /// @return the size (i.e. number of values) of a single component
271 269 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
272 270 int size() const
273 271 {
274 272 QReadLocker locker{&m_Lock};
275 273 return m_Data.size() / m_NbComponents;
276 274 }
277 275
278 276 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
279 277 {
280 278 QReadLocker locker{&m_Lock};
281 279 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
282 280 }
283 281
284 282 // ///////// //
285 283 // Iterators //
286 284 // ///////// //
287 285
288 286 ArrayDataIterator begin()
289 287 {
290 288 return ArrayDataIterator{
291 289 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
292 290 m_Data, m_NbComponents, true)}};
293 291 }
294 292
295 293 ArrayDataIterator end()
296 294 {
297 295 return ArrayDataIterator{
298 296 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
299 297 m_Data, m_NbComponents, false)}};
300 298 }
301 299
302 300 ArrayDataIterator cbegin() const
303 301 {
304 302 return ArrayDataIterator{
305 303 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
306 304 m_Data, m_NbComponents, true)}};
307 305 }
308 306
309 307 ArrayDataIterator cend() const
310 308 {
311 309 return ArrayDataIterator{
312 310 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
313 311 m_Data, m_NbComponents, false)}};
314 312 }
315 313
316 314 void erase(ArrayDataIterator first, ArrayDataIterator last)
317 315 {
318 316 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
319 317 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
320 318
321 319 if (firstImpl && lastImpl) {
322 320 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
323 321 }
324 322 }
325 323
326 324 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
327 325 {
328 326 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
329 327 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
330 328
331 329 if (firstImpl && lastImpl) {
332 330 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
333 331
334 332 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
335 333 }
336 334 }
337 335
338 336 /**
339 337 * @return the data at a specified index
340 338 * @remarks index must be a valid position
341 339 */
342 340 double at(int index) const noexcept
343 341 {
344 342 QReadLocker locker{&m_Lock};
345 343 return m_Data.at(index);
346 344 }
347 345
348 346 // ///////////// //
349 347 // 1-dim methods //
350 348 // ///////////// //
351 349
352 350 /**
353 351 * @return the data as a vector, as a const reference
354 352 * @remarks this method is only available for a unidimensional ArrayData
355 353 */
356 354 template <int D = Dim, typename = std::enable_if_t<D == 1> >
357 355 DataContainer cdata() const noexcept
358 356 {
359 357 return m_Data;
360 358 }
361 359
362 360 private:
363 361 DataContainer m_Data;
364 362 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
365 363 int m_NbComponents;
366 364 mutable QReadWriteLock m_Lock;
367 365 };
368 366
369 367 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,74 +1,74
1 1 #ifndef SCIQLOP_ARRAYDATAITERATOR_H
2 2 #define SCIQLOP_ARRAYDATAITERATOR_H
3 3
4 4 #include "CoreGlobal.h"
5 5 #include "Data/SqpIterator.h"
6 6
7 7 #include <QVector>
8 8 #include <memory>
9 9
10 10 /**
11 11 * @brief The ArrayDataIteratorValue class represents the current value of an array data iterator.
12 12 * It offers standard access methods for the data in the series (at(), first()), but it is up to
13 13 * each array data to define its own implementation of how to retrieve this data (one-dim or two-dim
14 14 * array), by implementing the ArrayDataIteratorValue::Impl interface
15 15 * @sa ArrayDataIterator
16 16 */
17 17 class SCIQLOP_CORE_EXPORT ArrayDataIteratorValue {
18 18 public:
19 19 struct Impl {
20 20 virtual ~Impl() noexcept = default;
21 21 virtual std::unique_ptr<Impl> clone() const = 0;
22 22 virtual int distance(const Impl &other) const = 0;
23 23 virtual bool equals(const Impl &other) const = 0;
24 24 virtual bool lowerThan(const Impl &other) const = 0;
25 25 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
26 virtual void next() = 0;
26 virtual void next(int offset) = 0;
27 27 virtual void prev() = 0;
28 28 virtual double at(int componentIndex) const = 0;
29 29 virtual double first() const = 0;
30 30 virtual double min() const = 0;
31 31 virtual double max() const = 0;
32 32 virtual QVector<double> values() const = 0;
33 33
34 34 virtual void swap(Impl &other) = 0;
35 35 };
36 36
37 37 explicit ArrayDataIteratorValue(std::unique_ptr<Impl> impl);
38 38 ArrayDataIteratorValue(const ArrayDataIteratorValue &other);
39 39 ArrayDataIteratorValue &operator=(ArrayDataIteratorValue other);
40 40
41 41 int distance(const ArrayDataIteratorValue &other) const;
42 42 bool equals(const ArrayDataIteratorValue &other) const;
43 43 bool lowerThan(const ArrayDataIteratorValue &other) const;
44 44
45 45 ArrayDataIteratorValue advance(int offset) const;
46 46 /// Advances to the next value
47 void next();
47 void next(int offset = 1);
48 48 /// Moves back to the previous value
49 49 void prev();
50 50 /// Gets value of a specified component
51 51 double at(int componentIndex) const;
52 52 /// Gets value of first component
53 53 double first() const;
54 54 /// Gets min value among all components
55 55 double min() const;
56 56 /// Gets max value among all components
57 57 double max() const;
58 58 /// Gets all values
59 59 QVector<double> values() const;
60 60
61 61 Impl *impl();
62 62
63 63 friend void swap(ArrayDataIteratorValue &lhs, ArrayDataIteratorValue &rhs)
64 64 {
65 65 std::swap(lhs.m_Impl, rhs.m_Impl);
66 66 }
67 67
68 68 private:
69 69 std::unique_ptr<Impl> m_Impl;
70 70 };
71 71
72 72 using ArrayDataIterator = SqpIterator<ArrayDataIteratorValue>;
73 73
74 74 #endif // SCIQLOP_ARRAYDATAITERATOR_H
@@ -1,401 +1,399
1 1 #ifndef SCIQLOP_DATASERIES_H
2 2 #define SCIQLOP_DATASERIES_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Common/SortUtils.h>
7 7
8 8 #include <Data/ArrayData.h>
9 9 #include <Data/DataSeriesMergeHelper.h>
10 10 #include <Data/IDataSeries.h>
11 11
12 12 #include <QLoggingCategory>
13 13 #include <QReadLocker>
14 14 #include <QReadWriteLock>
15 15 #include <memory>
16 16
17 17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 18 // definitions with inheritance. Inline method is used instead
19 19 inline const QLoggingCategory &LOG_DataSeries()
20 20 {
21 21 static const QLoggingCategory category{"DataSeries"};
22 22 return category;
23 23 }
24 24
25 25 template <int Dim>
26 26 class DataSeries;
27 27
28 28 namespace dataseries_detail {
29 29
30 30 template <int Dim, bool IsConst>
31 31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 32 public:
33 33 friend class DataSeries<Dim>;
34 34
35 35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 39 {
40 40 }
41 41
42 42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 46 : dataSeries.valuesData()->cend())
47 47 {
48 48 }
49 49
50 50 IteratorValue(const IteratorValue &other) = default;
51 51
52 52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 53 {
54 54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 55 }
56 56
57 57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 59 return m_XIt->distance(*otherImpl.m_XIt);
60 60 }
61 61 catch (const std::bad_cast &) {
62 62 return 0;
63 63 }
64 64
65 65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 68 }
69 69 catch (const std::bad_cast &) {
70 70 return false;
71 71 }
72 72
73 73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 75 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 76 }
77 77 catch (const std::bad_cast &) {
78 78 return false;
79 79 }
80 80
81 81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 82 {
83 83 auto result = clone();
84 while (offset--) {
85 result->next();
86 }
84 result->next(offset);
87 85 return result;
88 86 }
89 87
90 void next() override
88 void next(int offset) override
91 89 {
92 ++m_XIt;
93 ++m_ValuesIt;
90 m_XIt->next(offset);
91 m_ValuesIt->next(offset);
94 92 }
95 93
96 94 void prev() override
97 95 {
98 96 --m_XIt;
99 97 --m_ValuesIt;
100 98 }
101 99
102 100 double x() const override { return m_XIt->at(0); }
103 101 double value() const override { return m_ValuesIt->at(0); }
104 102 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
105 103 double minValue() const override { return m_ValuesIt->min(); }
106 104 double maxValue() const override { return m_ValuesIt->max(); }
107 105 QVector<double> values() const override { return m_ValuesIt->values(); }
108 106
109 107 void swap(DataSeriesIteratorValue::Impl &other) override
110 108 {
111 109 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
112 110 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
113 111 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
114 112 }
115 113
116 114 private:
117 115 ArrayDataIterator m_XIt;
118 116 ArrayDataIterator m_ValuesIt;
119 117 };
120 118 } // namespace dataseries_detail
121 119
122 120 /**
123 121 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
124 122 *
125 123 * It proposes to set a dimension for the values ​​data.
126 124 *
127 125 * A DataSeries is always sorted on its x-axis data.
128 126 *
129 127 * @tparam Dim The dimension of the values data
130 128 *
131 129 */
132 130 template <int Dim>
133 131 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
134 132 friend class DataSeriesMergeHelper;
135 133
136 134 public:
137 135 /// @sa IDataSeries::xAxisData()
138 136 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
139 137 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
140 138
141 139 /// @sa IDataSeries::xAxisUnit()
142 140 Unit xAxisUnit() const override { return m_XAxisUnit; }
143 141
144 142 /// @return the values dataset
145 143 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
146 144 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
147 145
148 146 /// @sa IDataSeries::valuesUnit()
149 147 Unit valuesUnit() const override { return m_ValuesUnit; }
150 148
151 149
152 150 SqpRange range() const override
153 151 {
154 152 if (!m_XAxisData->cdata().empty()) {
155 153 return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()};
156 154 }
157 155
158 156 return SqpRange{};
159 157 }
160 158
161 159 void clear()
162 160 {
163 161 m_XAxisData->clear();
164 162 m_ValuesData->clear();
165 163 }
166 164
167 165 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
168 166
169 167 /// Merges into the data series an other data series
170 168 /// @remarks the data series to merge with is cleared after the operation
171 169 void merge(IDataSeries *dataSeries) override
172 170 {
173 171 dataSeries->lockWrite();
174 172 lockWrite();
175 173
176 174 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
177 175 DataSeriesMergeHelper::merge(*other, *this);
178 176 }
179 177 else {
180 178 qCWarning(LOG_DataSeries())
181 179 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
182 180 }
183 181 unlock();
184 182 dataSeries->unlock();
185 183 }
186 184
187 185 void purge(double min, double max) override
188 186 {
189 187 // Nothing to purge if series is empty
190 188 if (isEmpty()) {
191 189 return;
192 190 }
193 191
194 192 if (min > max) {
195 193 std::swap(min, max);
196 194 }
197 195
198 196 // Nothing to purge if series min/max are inside purge range
199 197 auto xMin = cbegin()->x();
200 198 auto xMax = (--cend())->x();
201 199 if (xMin >= min && xMax <= max) {
202 200 return;
203 201 }
204 202
205 203 auto lowerIt = std::lower_bound(
206 204 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
207 205 erase(begin(), lowerIt);
208 206 auto upperIt = std::upper_bound(
209 207 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
210 208 erase(upperIt, end());
211 209 }
212 210
213 211 // ///////// //
214 212 // Iterators //
215 213 // ///////// //
216 214
217 215 DataSeriesIterator begin() override
218 216 {
219 217 return DataSeriesIterator{DataSeriesIteratorValue{
220 218 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
221 219 }
222 220
223 221 DataSeriesIterator end() override
224 222 {
225 223 return DataSeriesIterator{DataSeriesIteratorValue{
226 224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
227 225 }
228 226
229 227 DataSeriesIterator cbegin() const override
230 228 {
231 229 return DataSeriesIterator{DataSeriesIteratorValue{
232 230 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
233 231 }
234 232
235 233 DataSeriesIterator cend() const override
236 234 {
237 235 return DataSeriesIterator{DataSeriesIteratorValue{
238 236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
239 237 }
240 238
241 239 void erase(DataSeriesIterator first, DataSeriesIterator last)
242 240 {
243 241 auto firstImpl
244 242 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
245 243 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
246 244
247 245 if (firstImpl && lastImpl) {
248 246 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
249 247 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
250 248 }
251 249 }
252 250
253 251 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
254 252 {
255 253 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
256 254 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
257 255
258 256 if (firstImpl && lastImpl) {
259 257 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
260 258 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
261 259 }
262 260 }
263 261
264 262 /// @sa IDataSeries::minXAxisData()
265 263 DataSeriesIterator minXAxisData(double minXAxisData) const override
266 264 {
267 265 return std::lower_bound(
268 266 cbegin(), cend(), minXAxisData,
269 267 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
270 268 }
271 269
272 270 /// @sa IDataSeries::maxXAxisData()
273 271 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
274 272 {
275 273 // Gets the first element that greater than max value
276 274 auto it = std::upper_bound(
277 275 cbegin(), cend(), maxXAxisData,
278 276 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
279 277
280 278 return it == cbegin() ? cend() : --it;
281 279 }
282 280
283 281 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
284 282 double maxXAxisData) const override
285 283 {
286 284 if (minXAxisData > maxXAxisData) {
287 285 std::swap(minXAxisData, maxXAxisData);
288 286 }
289 287
290 288 auto begin = cbegin();
291 289 auto end = cend();
292 290
293 291 auto lowerIt = std::lower_bound(
294 292 begin, end, minXAxisData,
295 293 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
296 294 auto upperIt = std::upper_bound(
297 295 lowerIt, end, maxXAxisData,
298 296 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
299 297
300 298 return std::make_pair(lowerIt, upperIt);
301 299 }
302 300
303 301 std::pair<DataSeriesIterator, DataSeriesIterator>
304 302 valuesBounds(double minXAxisData, double maxXAxisData) const override
305 303 {
306 304 // Places iterators to the correct x-axis range
307 305 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
308 306
309 307 // Returns end iterators if the range is empty
310 308 if (xAxisRangeIts.first == xAxisRangeIts.second) {
311 309 return std::make_pair(cend(), cend());
312 310 }
313 311
314 312 // Gets the iterator on the min of all values data
315 313 auto minIt = std::min_element(
316 314 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
317 315 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
318 316 });
319 317
320 318 // Gets the iterator on the max of all values data
321 319 auto maxIt = std::max_element(
322 320 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
323 321 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
324 322 });
325 323
326 324 return std::make_pair(minIt, maxIt);
327 325 }
328 326
329 327 // /////// //
330 328 // Mutexes //
331 329 // /////// //
332 330
333 331 virtual void lockRead() { m_Lock.lockForRead(); }
334 332 virtual void lockWrite() { m_Lock.lockForWrite(); }
335 333 virtual void unlock() { m_Lock.unlock(); }
336 334
337 335 protected:
338 336 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
339 337 /// DataSeries with no values will be created.
340 338 /// @remarks data series is automatically sorted on its x-axis data
341 339 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
342 340 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
343 341 : m_XAxisData{xAxisData},
344 342 m_XAxisUnit{xAxisUnit},
345 343 m_ValuesData{valuesData},
346 344 m_ValuesUnit{valuesUnit}
347 345 {
348 346 if (m_XAxisData->size() != m_ValuesData->size()) {
349 347 clear();
350 348 }
351 349
352 350 // Sorts data if it's not the case
353 351 const auto &xAxisCData = m_XAxisData->cdata();
354 352 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
355 353 sort();
356 354 }
357 355 }
358 356
359 357 /// Copy ctor
360 358 explicit DataSeries(const DataSeries<Dim> &other)
361 359 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
362 360 m_XAxisUnit{other.m_XAxisUnit},
363 361 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
364 362 m_ValuesUnit{other.m_ValuesUnit}
365 363 {
366 364 // Since a series is ordered from its construction and is always ordered, it is not
367 365 // necessary to call the sort method here ('other' is sorted)
368 366 }
369 367
370 368 /// Assignment operator
371 369 template <int D>
372 370 DataSeries &operator=(DataSeries<D> other)
373 371 {
374 372 std::swap(m_XAxisData, other.m_XAxisData);
375 373 std::swap(m_XAxisUnit, other.m_XAxisUnit);
376 374 std::swap(m_ValuesData, other.m_ValuesData);
377 375 std::swap(m_ValuesUnit, other.m_ValuesUnit);
378 376
379 377 return *this;
380 378 }
381 379
382 380 private:
383 381 /**
384 382 * Sorts data series on its x-axis data
385 383 */
386 384 void sort() noexcept
387 385 {
388 386 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
389 387 m_XAxisData = m_XAxisData->sort(permutation);
390 388 m_ValuesData = m_ValuesData->sort(permutation);
391 389 }
392 390
393 391 std::shared_ptr<ArrayData<1> > m_XAxisData;
394 392 Unit m_XAxisUnit;
395 393 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
396 394 Unit m_ValuesUnit;
397 395
398 396 QReadWriteLock m_Lock;
399 397 };
400 398
401 399 #endif // SCIQLOP_DATASERIES_H
@@ -1,78 +1,78
1 1 #ifndef SCIQLOP_DATASERIESITERATOR_H
2 2 #define SCIQLOP_DATASERIESITERATOR_H
3 3
4 4 #include "CoreGlobal.h"
5 5 #include "Data/SqpIterator.h"
6 6
7 7 #include <QVector>
8 8 #include <memory>
9 9
10 10 /**
11 11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
12 12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
13 13 * each series to define its own implementation of how to retrieve this data, by implementing the
14 14 * DataSeriesIteratorValue::Impl interface
15 15 *
16 16 * @sa DataSeriesIterator
17 17 */
18 18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
19 19 public:
20 20 struct Impl {
21 21 virtual ~Impl() noexcept = default;
22 22 virtual std::unique_ptr<Impl> clone() const = 0;
23 23 virtual int distance(const Impl &other) const = 0;
24 24 virtual bool equals(const Impl &other) const = 0;
25 25 virtual bool lowerThan(const Impl &other) const = 0;
26 26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
27 virtual void next() = 0;
27 virtual void next(int offset) = 0;
28 28 virtual void prev() = 0;
29 29 virtual double x() const = 0;
30 30 virtual double value() const = 0;
31 31 virtual double value(int componentIndex) const = 0;
32 32 virtual double minValue() const = 0;
33 33 virtual double maxValue() const = 0;
34 34 virtual QVector<double> values() const = 0;
35 35
36 36 virtual void swap(Impl &other) = 0;
37 37 };
38 38
39 39 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
40 40 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
41 41 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
42 42
43 43 int distance(const DataSeriesIteratorValue &other) const;
44 44 bool equals(const DataSeriesIteratorValue &other) const;
45 45 bool lowerThan(const DataSeriesIteratorValue &other) const;
46 46
47 47 DataSeriesIteratorValue advance(int offset) const;
48 48 /// Advances to the next value
49 void next();
49 void next(int offset = 1);
50 50 /// Moves back to the previous value
51 51 void prev();
52 52 /// Gets x-axis data
53 53 double x() const;
54 54 /// Gets value data
55 55 double value() const;
56 56 /// Gets value data depending on an index
57 57 double value(int componentIndex) const;
58 58 /// Gets min of all values data
59 59 double minValue() const;
60 60 /// Gets max of all values data
61 61 double maxValue() const;
62 62 /// Gets all values data
63 63 QVector<double> values() const;
64 64
65 65 Impl *impl();
66 66
67 67 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
68 68 {
69 69 std::swap(lhs.m_Impl, rhs.m_Impl);
70 70 }
71 71
72 72 private:
73 73 std::unique_ptr<Impl> m_Impl;
74 74 };
75 75
76 76 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
77 77
78 78 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -1,110 +1,108
1 1 #ifndef SCIQLOP_SQPITERATOR_H
2 2 #define SCIQLOP_SQPITERATOR_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 /**
7 7 * @brief The SqpIterator class represents an iterator used in SciQlop. It defines all operators
8 8 * needed for a standard forward iterator
9 9 * @tparam T the type of object handled in iterator
10 10 * @sa http://www.cplusplus.com/reference/iterator/
11 11 */
12 12 template <typename T>
13 13 class SCIQLOP_CORE_EXPORT SqpIterator {
14 14 public:
15 15 using iterator_category = std::random_access_iterator_tag;
16 16 using value_type = const T;
17 17 using difference_type = std::ptrdiff_t;
18 18 using pointer = value_type *;
19 19 using reference = value_type &;
20 20
21 21 explicit SqpIterator(T value) : m_CurrentValue{std::move(value)} {}
22 22
23 23 virtual ~SqpIterator() noexcept = default;
24 24 SqpIterator(const SqpIterator &) = default;
25 25 SqpIterator &operator=(SqpIterator other) { swap(m_CurrentValue, other.m_CurrentValue); }
26 26
27 27 SqpIterator &operator++()
28 28 {
29 29 m_CurrentValue.next();
30 30 return *this;
31 31 }
32 32
33 33 SqpIterator &operator--()
34 34 {
35 35 m_CurrentValue.prev();
36 36 return *this;
37 37 }
38 38
39 39 SqpIterator operator++(int)const
40 40 {
41 41 auto result = *this;
42 42 this->operator++();
43 43 return result;
44 44 }
45 45 SqpIterator operator--(int)const
46 46 {
47 47 auto result = *this;
48 48 this->operator--();
49 49 return result;
50 50 }
51 51
52 52 SqpIterator &operator+=(int offset)
53 53 {
54 54 if (offset >= 0) {
55 while (offset--) {
56 m_CurrentValue.next();
57 }
55 m_CurrentValue.next(offset);
58 56 }
59 57 else {
60 58 while (offset++) {
61 59 m_CurrentValue.prev();
62 60 }
63 61 }
64 62
65 63 return *this;
66 64 }
67 65 SqpIterator &operator-=(int offset) { return *this += -offset; }
68 66
69 67 SqpIterator operator+(int offset) const
70 68 {
71 69 auto result = *this;
72 70 result += offset;
73 71 return result;
74 72 }
75 73 SqpIterator operator-(int offset) const
76 74 {
77 75 auto result = *this;
78 76 result -= offset;
79 77 return result;
80 78 }
81 79
82 80 int operator-(const SqpIterator &other) const
83 81 {
84 82 return m_CurrentValue.distance(other.m_CurrentValue);
85 83 }
86 84
87 85 const T *operator->() const { return &m_CurrentValue; }
88 86 const T &operator*() const { return m_CurrentValue; }
89 87 T *operator->() { return &m_CurrentValue; }
90 88 T &operator*() { return m_CurrentValue; }
91 89 T &operator[](int offset) const { return m_CurrentValue.advance(offset); }
92 90
93 91 bool operator==(const SqpIterator &other) const
94 92 {
95 93 return m_CurrentValue.equals(other.m_CurrentValue);
96 94 }
97 95 bool operator!=(const SqpIterator &other) const { return !(*this == other); }
98 96 bool operator>(const SqpIterator &other) const { return other.m_CurrentValue.lowerThan(*this); }
99 97 bool operator<(const SqpIterator &other) const
100 98 {
101 99 return m_CurrentValue.lowerThan(other.m_CurrentValue);
102 100 }
103 101 bool operator>=(const SqpIterator &other) const { return !(*this < other); }
104 102 bool operator<=(const SqpIterator &other) const { return !(*this > other); }
105 103
106 104 private:
107 105 T m_CurrentValue;
108 106 };
109 107
110 108 #endif // SCIQLOP_SQPITERATOR_H
@@ -1,77 +1,77
1 1 #include "Data/ArrayDataIterator.h"
2 2
3 3 ArrayDataIteratorValue::ArrayDataIteratorValue(std::unique_ptr<ArrayDataIteratorValue::Impl> impl)
4 4 : m_Impl{std::move(impl)}
5 5 {
6 6 }
7 7
8 8 ArrayDataIteratorValue::ArrayDataIteratorValue(const ArrayDataIteratorValue &other)
9 9 : m_Impl{other.m_Impl->clone()}
10 10 {
11 11 }
12 12
13 13 ArrayDataIteratorValue &ArrayDataIteratorValue::operator=(ArrayDataIteratorValue other)
14 14 {
15 15 m_Impl->swap(*other.m_Impl);
16 16 return *this;
17 17 }
18 18
19 19 int ArrayDataIteratorValue::distance(const ArrayDataIteratorValue &other) const
20 20 {
21 21 return m_Impl->distance(*other.m_Impl);
22 22 }
23 23
24 24 bool ArrayDataIteratorValue::equals(const ArrayDataIteratorValue &other) const
25 25 {
26 26 return m_Impl->equals(*other.m_Impl);
27 27 }
28 28
29 29 bool ArrayDataIteratorValue::lowerThan(const ArrayDataIteratorValue &other) const
30 30 {
31 31 return m_Impl->lowerThan(*other.m_Impl);
32 32 }
33 33
34 34 ArrayDataIteratorValue ArrayDataIteratorValue::advance(int offset) const
35 35 {
36 36 return ArrayDataIteratorValue{m_Impl->advance(offset)};
37 37 }
38 38
39 void ArrayDataIteratorValue::next()
39 void ArrayDataIteratorValue::next(int offset)
40 40 {
41 m_Impl->next();
41 m_Impl->next(offset);
42 42 }
43 43
44 44 void ArrayDataIteratorValue::prev()
45 45 {
46 46 m_Impl->prev();
47 47 }
48 48
49 49 double ArrayDataIteratorValue::at(int componentIndex) const
50 50 {
51 51 return m_Impl->at(componentIndex);
52 52 }
53 53
54 54 double ArrayDataIteratorValue::first() const
55 55 {
56 56 return m_Impl->first();
57 57 }
58 58
59 59 double ArrayDataIteratorValue::min() const
60 60 {
61 61 return m_Impl->min();
62 62 }
63 63
64 64 double ArrayDataIteratorValue::max() const
65 65 {
66 66 return m_Impl->max();
67 67 }
68 68
69 69 QVector<double> ArrayDataIteratorValue::values() const
70 70 {
71 71 return m_Impl->values();
72 72 }
73 73
74 74 ArrayDataIteratorValue::Impl *ArrayDataIteratorValue::impl()
75 75 {
76 76 return m_Impl.get();
77 77 }
@@ -1,84 +1,84
1 1 #include "Data/DataSeriesIterator.h"
2 2
3 3 DataSeriesIteratorValue::DataSeriesIteratorValue(
4 4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
5 5 : m_Impl{std::move(impl)}
6 6 {
7 7 }
8 8
9 9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
10 10 : m_Impl{other.m_Impl->clone()}
11 11 {
12 12 }
13 13
14 14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
15 15 {
16 16 m_Impl->swap(*other.m_Impl);
17 17 return *this;
18 18 }
19 19
20 20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
21 21 {
22 22 auto dist = m_Impl->distance(*other.m_Impl);
23 23 return m_Impl->distance(*other.m_Impl);
24 24 }
25 25
26 26 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
27 27 {
28 28 return m_Impl->equals(*other.m_Impl);
29 29 }
30 30
31 31 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
32 32 {
33 33 return m_Impl->lowerThan(*other.m_Impl);
34 34 }
35 35
36 36 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
37 37 {
38 38 return DataSeriesIteratorValue{m_Impl->advance(offset)};
39 39 }
40 40
41 void DataSeriesIteratorValue::next()
41 void DataSeriesIteratorValue::next(int offset)
42 42 {
43 m_Impl->next();
43 m_Impl->next(offset);
44 44 }
45 45
46 46 void DataSeriesIteratorValue::prev()
47 47 {
48 48 m_Impl->prev();
49 49 }
50 50
51 51 double DataSeriesIteratorValue::x() const
52 52 {
53 53 return m_Impl->x();
54 54 }
55 55
56 56 double DataSeriesIteratorValue::value() const
57 57 {
58 58 return m_Impl->value();
59 59 }
60 60
61 61 double DataSeriesIteratorValue::value(int componentIndex) const
62 62 {
63 63 return m_Impl->value(componentIndex);
64 64 }
65 65
66 66 double DataSeriesIteratorValue::minValue() const
67 67 {
68 68 return m_Impl->minValue();
69 69 }
70 70
71 71 double DataSeriesIteratorValue::maxValue() const
72 72 {
73 73 return m_Impl->maxValue();
74 74 }
75 75
76 76 QVector<double> DataSeriesIteratorValue::values() const
77 77 {
78 78 return m_Impl->values();
79 79 }
80 80
81 81 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
82 82 {
83 83 return m_Impl.get();
84 84 }
General Comments 0
You need to be logged in to leave comments. Login now