##// END OF EJS Templates
Updates ArrayData iterator to be use single QVector
Alexandre Leroux -
r601:6cf9aea49f46
parent child
Show More
@@ -1,296 +1,298
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 = QVector<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>
43 43 class IteratorValue : public ArrayDataIteratorValue::Impl {
44 44 public:
45 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}
46 47 {
47 for (auto i = 0; i < container.size(); ++i) {
48 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
49 }
50 48 }
51 49
52 50 IteratorValue(const IteratorValue &other) = default;
53 51
54 52 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
55 53 {
56 54 return std::make_unique<IteratorValue<Dim> >(*this);
57 55 }
58 56
59 57 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
60 58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
61 return m_Its == otherImpl.m_Its;
59 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
62 60 }
63 61 catch (const std::bad_cast &) {
64 62 return false;
65 63 }
66 64
67 void next() override
68 {
69 for (auto &it : m_Its) {
70 ++it;
71 }
72 }
73
74 void prev() override
75 {
76 for (auto &it : m_Its) {
77 --it;
78 }
79 }
65 void next() override { std::advance(m_It, m_NbComponents); }
66 void prev() override { std::advance(m_It, -m_NbComponents); }
80 67
81 double at(int componentIndex) const override { return *m_Its.at(componentIndex); }
82 double first() const override { return *m_Its.front(); }
68 double at(int componentIndex) const override { return *(m_It + componentIndex); }
69 double first() const override { return *m_It; }
83 70 double min() const override
84 71 {
85 auto end = m_Its.cend();
86 auto it = std::min_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
87 return SortUtils::minCompareWithNaN(*it1, *it2);
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);
88 76 });
89 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
77
78 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
90 79 }
91 80 double max() const override
92 81 {
93 auto end = m_Its.cend();
94 auto it = std::max_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
95 return SortUtils::maxCompareWithNaN(*it1, *it2);
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);
96 86 });
97 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
87 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
98 88 }
99 89
100 90 private:
101 std::vector<DataContainer::value_type::const_iterator> m_Its;
91 std::vector<double> values() const
92 {
93 auto result = std::vector<double>{};
94 for (auto i = 0; i < m_NbComponents; ++i) {
note

You can resize result to m_NbCompoenent then use [i] = instead of push_back

95 result.push_back(*(m_It + i));
96 }
97
98 return result;
99 }
100
101 DataContainer::const_iterator m_It;
102 int m_NbComponents;
102 103 };
103 104
104 105 } // namespace arraydata_detail
105 106
106 107 /**
107 108 * @brief The ArrayData class represents a dataset for a data series.
108 109 *
109 110 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
110 111 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
111 112 * number of values
112 113 *
113 114 * @tparam Dim the dimension of the ArrayData (one or two)
114 115 * @sa IDataSeries
115 116 */
116 117 template <int Dim>
117 118 class ArrayData {
118 119 public:
119 120 // ///// //
120 121 // Ctors //
121 122 // ///// //
122 123
123 124 /**
124 125 * Ctor for a unidimensional ArrayData
125 126 * @param data the data the ArrayData will hold
126 127 */
127 128 template <int D = Dim, typename = std::enable_if_t<D == 1> >
128 129 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
129 130 {
130 131 }
131 132
132 133 /**
133 134 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
134 135 * greater than 2 and must be a divisor of the total number of data in the vector
135 136 * @param data the data the ArrayData will hold
136 137 * @param nbComponents the number of components
137 138 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
138 139 * of the size of the data
139 140 */
140 141 template <int D = Dim, typename = std::enable_if_t<D == 2> >
141 142 explicit ArrayData(DataContainer data, int nbComponents)
142 143 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
143 144 {
144 145 if (nbComponents < 2) {
145 146 throw std::invalid_argument{
146 147 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
147 148 .arg(nbComponents)
148 149 .toStdString()};
149 150 }
150 151
151 152 if (m_Data.size() % m_NbComponents != 0) {
152 153 throw std::invalid_argument{QString{
153 154 "The number of components (%1) is inconsistent with the total number of data (%2)"}
154 155 .arg(m_Data.size(), nbComponents)
155 156 .toStdString()};
156 157 }
157 158 }
158 159
159 160 /// Copy ctor
160 161 explicit ArrayData(const ArrayData &other)
161 162 {
162 163 QReadLocker otherLocker{&other.m_Lock};
163 164 m_Data = other.m_Data;
164 165 m_NbComponents = other.m_NbComponents;
165 166 }
166 167
167 168 // /////////////// //
168 169 // General methods //
169 170 // /////////////// //
170 171
171 172 /**
172 173 * Merges into the array data an other array data. The two array datas must have the same number
173 174 * of components so the merge can be done
174 175 * @param other the array data to merge with
175 176 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
176 177 * inserted at the end
177 178 */
178 179 void add(const ArrayData<Dim> &other, bool prepend = false)
179 180 {
180 181 QWriteLocker locker{&m_Lock};
181 182 QReadLocker otherLocker{&other.m_Lock};
182 183
183 184 if (m_NbComponents != other.componentCount()) {
184 185 return;
185 186 }
186 187
187 188 if (prepend) {
188 189 auto otherDataSize = other.m_Data.size();
189 190 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
190 191 for (auto i = 0; i < otherDataSize; ++i) {
191 192 m_Data.replace(i, other.m_Data.at(i));
192 193 }
193 194 }
194 195 else {
195 196 m_Data.append(other.m_Data);
196 197 }
197 198 }
198 199
199 200 void clear()
200 201 {
201 202 QWriteLocker locker{&m_Lock};
202 203 m_Data.clear();
203 204 }
204 205
205 206 int componentCount() const noexcept { return m_NbComponents; }
206 207
207 208 /// @return the size (i.e. number of values) of a single component
208 209 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
209 210 int size() const
210 211 {
211 212 QReadLocker locker{&m_Lock};
212 213 return m_Data.size() / m_NbComponents;
213 214 }
214 215
215 216 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
216 217 {
217 218 QReadLocker locker{&m_Lock};
218 219 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
219 220 }
220 221
221 222 // ///////// //
222 223 // Iterators //
223 224 // ///////// //
224 225
225 226 ArrayDataIterator cbegin() const
226 227 {
227 228 return ArrayDataIterator{ArrayDataIteratorValue{
228 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, true)}};
229 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, m_NbComponents, true)}};
229 230 }
230 231 ArrayDataIterator cend() const
231 232 {
232 return ArrayDataIterator{ArrayDataIteratorValue{
233 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, false)}};
233 return ArrayDataIterator{
234 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim> >(
235 m_Data, m_NbComponents, false)}};
234 236 }
235 237
236 238 // ///////////// //
237 239 // 1-dim methods //
238 240 // ///////////// //
239 241
240 242 /**
241 243 * @return the data at a specified index
242 244 * @remarks index must be a valid position
243 245 * @remarks this method is only available for a unidimensional ArrayData
244 246 */
245 247 template <int D = Dim, typename = std::enable_if_t<D == 1> >
246 248 double at(int index) const noexcept
247 249 {
248 250 QReadLocker locker{&m_Lock};
249 251 return m_Data.at(index);
250 252 }
251 253
252 254 /**
253 255 * @return the data as a vector, as a const reference
254 256 * @remarks this method is only available for a unidimensional ArrayData
255 257 */
256 258 template <int D = Dim, typename = std::enable_if_t<D == 1> >
257 259 const QVector<double> &cdata() const noexcept
258 260 {
259 261 QReadLocker locker{&m_Lock};
260 262 return m_Data.at(0);
261 263 }
262 264
263 265 /**
264 266 * @return the data as a vector
265 267 * @remarks this method is only available for a unidimensional ArrayData
266 268 */
267 269 template <int D = Dim, typename = std::enable_if_t<D == 1> >
268 270 QVector<double> data() const noexcept
269 271 {
270 272 QReadLocker locker{&m_Lock};
271 273 return m_Data[0];
272 274 }
273 275
274 276 // ///////////// //
275 277 // 2-dim methods //
276 278 // ///////////// //
277 279
278 280 /**
279 281 * @return the data
280 282 * @remarks this method is only available for a two-dimensional ArrayData
281 283 */
282 284 template <int D = Dim, typename = std::enable_if_t<D == 2> >
283 285 DataContainer data() const noexcept
284 286 {
285 287 QReadLocker locker{&m_Lock};
286 288 return m_Data;
287 289 }
288 290
289 291 private:
290 292 DataContainer m_Data;
291 293 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
292 294 int m_NbComponents;
293 295 mutable QReadWriteLock m_Lock;
294 296 };
295 297
296 298 #endif // SCIQLOP_ARRAYDATA_H
General Comments 0
You need to be logged in to leave comments. Login now