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