##// END OF EJS Templates
Minor changes...
Alexandre Leroux -
r864:551a1137cf04
parent child
Show More
@@ -0,0 +1,23
1 #ifndef SCIQLOP_UNIT_H
2 #define SCIQLOP_UNIT_H
3
4 #include <QString>
5 #include <tuple>
6
7 struct Unit {
8 explicit Unit(const QString &name = {}, bool timeUnit = false)
9 : m_Name{name}, m_TimeUnit{timeUnit}
10 {
11 }
12
13 inline bool operator==(const Unit &other) const
14 {
15 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
16 }
17 inline bool operator!=(const Unit &other) const { return !(*this == other); }
18
19 QString m_Name; ///< Unit name
20 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
21 };
22
23 #endif // SCIQLOP_UNIT_H
@@ -1,374 +1,373
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 = std::vector<double>;
16 using DataContainer = std::vector<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, int nbComponents,
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
24 const std::vector<int> &sortPermutation)
24 const std::vector<int> &sortPermutation)
25 {
25 {
26 return std::make_shared<ArrayData<Dim> >(
26 return std::make_shared<ArrayData<Dim> >(
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
28 }
28 }
29 };
29 };
30
30
31 /// Specialization for uni-dimensional ArrayData
31 /// Specialization for uni-dimensional ArrayData
32 template <>
32 template <>
33 struct Sort<1> {
33 struct Sort<1> {
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
35 const std::vector<int> &sortPermutation)
35 const std::vector<int> &sortPermutation)
36 {
36 {
37 Q_UNUSED(nbComponents)
37 Q_UNUSED(nbComponents)
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
39 }
39 }
40 };
40 };
41
41
42 template <int Dim, bool IsConst>
42 template <int Dim, bool IsConst>
43 class IteratorValue;
43 class IteratorValue;
44
44
45 template <int Dim, bool IsConst>
45 template <int Dim, bool IsConst>
46 struct IteratorValueBuilder {
46 struct IteratorValueBuilder {
47 };
47 };
48
48
49 template <int Dim>
49 template <int Dim>
50 struct IteratorValueBuilder<Dim, true> {
50 struct IteratorValueBuilder<Dim, true> {
51 using DataContainerIterator = DataContainer::const_iterator;
51 using DataContainerIterator = DataContainer::const_iterator;
52
52
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
54 };
54 };
55
55
56 template <int Dim>
56 template <int Dim>
57 struct IteratorValueBuilder<Dim, false> {
57 struct IteratorValueBuilder<Dim, false> {
58 using DataContainerIterator = DataContainer::iterator;
58 using DataContainerIterator = DataContainer::iterator;
59
59
60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
61 {
61 {
62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
63 std::iter_swap(o1.m_It + i, o2.m_It + i);
63 std::iter_swap(o1.m_It + i, o2.m_It + i);
64 }
64 }
65 }
65 }
66 };
66 };
67
67
68 template <int Dim, bool IsConst>
68 template <int Dim, bool IsConst>
69 class IteratorValue : public ArrayDataIteratorValue::Impl {
69 class IteratorValue : public ArrayDataIteratorValue::Impl {
70 public:
70 public:
71 friend class ArrayData<Dim>;
71 friend class ArrayData<Dim>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
73
73
74 using DataContainerIterator =
74 using DataContainerIterator =
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
76
76
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
80 {
80 {
81 }
81 }
82
82
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
86 {
86 {
87 }
87 }
88
88
89 IteratorValue(const IteratorValue &other) = default;
89 IteratorValue(const IteratorValue &other) = default;
90
90
91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
92 {
92 {
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
94 }
94 }
95
95
96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
97 /// @todo ALX : validate
98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
97 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
98 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
100 }
99 }
101 catch (const std::bad_cast &) {
100 catch (const std::bad_cast &) {
102 return 0;
101 return 0;
103 }
102 }
104
103
105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
104 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
105 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
106 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
108 }
107 }
109 catch (const std::bad_cast &) {
108 catch (const std::bad_cast &) {
110 return false;
109 return false;
111 }
110 }
112
111
113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
112 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
113 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
115 return m_It < otherImpl.m_It;
114 return m_It < otherImpl.m_It;
116 }
115 }
117 catch (const std::bad_cast &) {
116 catch (const std::bad_cast &) {
118 return false;
117 return false;
119 }
118 }
120
119
121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
120 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
122 {
121 {
123 auto result = clone();
122 auto result = clone();
124 result->next(offset);
123 result->next(offset);
125 return result;
124 return result;
126 }
125 }
127
126
128 void next(int offset) override { std::advance(m_It, offset * m_NbComponents); }
127 void next(int offset) override { std::advance(m_It, offset * m_NbComponents); }
129 void prev() override { std::advance(m_It, -m_NbComponents); }
128 void prev() override { std::advance(m_It, -m_NbComponents); }
130
129
131 double at(int componentIndex) const override { return *(m_It + componentIndex); }
130 double at(int componentIndex) const override { return *(m_It + componentIndex); }
132 double first() const override { return *m_It; }
131 double first() const override { return *m_It; }
133 double min() const override
132 double min() const override
134 {
133 {
135 auto values = this->values();
134 auto values = this->values();
136 auto end = values.cend();
135 auto end = values.cend();
137 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
136 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
138 return SortUtils::minCompareWithNaN(v1, v2);
137 return SortUtils::minCompareWithNaN(v1, v2);
139 });
138 });
140
139
141 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
140 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
142 }
141 }
143 double max() const override
142 double max() const override
144 {
143 {
145 auto values = this->values();
144 auto values = this->values();
146 auto end = values.cend();
145 auto end = values.cend();
147 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
146 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
148 return SortUtils::maxCompareWithNaN(v1, v2);
147 return SortUtils::maxCompareWithNaN(v1, v2);
149 });
148 });
150 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
149 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
151 }
150 }
152
151
153 QVector<double> values() const override
152 QVector<double> values() const override
154 {
153 {
155 auto result = QVector<double>{};
154 auto result = QVector<double>{};
156 for (auto i = 0; i < m_NbComponents; ++i) {
155 for (auto i = 0; i < m_NbComponents; ++i) {
157 result.push_back(*(m_It + i));
156 result.push_back(*(m_It + i));
158 }
157 }
159
158
160 return result;
159 return result;
161 }
160 }
162
161
163 void swap(ArrayDataIteratorValue::Impl &other) override
162 void swap(ArrayDataIteratorValue::Impl &other) override
164 {
163 {
165 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
164 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
166 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
165 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
167 }
166 }
168
167
169 private:
168 private:
170 DataContainerIterator m_It;
169 DataContainerIterator m_It;
171 int m_NbComponents;
170 int m_NbComponents;
172 };
171 };
173
172
174 } // namespace arraydata_detail
173 } // namespace arraydata_detail
175
174
176 /**
175 /**
177 * @brief The ArrayData class represents a dataset for a data series.
176 * @brief The ArrayData class represents a dataset for a data series.
178 *
177 *
179 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
178 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
180 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
179 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
181 * number of values
180 * number of values
182 *
181 *
183 * @tparam Dim the dimension of the ArrayData (one or two)
182 * @tparam Dim the dimension of the ArrayData (one or two)
184 * @sa IDataSeries
183 * @sa IDataSeries
185 */
184 */
186 template <int Dim>
185 template <int Dim>
187 class ArrayData {
186 class ArrayData {
188 public:
187 public:
189 // ///// //
188 // ///// //
190 // Ctors //
189 // Ctors //
191 // ///// //
190 // ///// //
192
191
193 /**
192 /**
194 * Ctor for a unidimensional ArrayData
193 * Ctor for a unidimensional ArrayData
195 * @param data the data the ArrayData will hold
194 * @param data the data the ArrayData will hold
196 */
195 */
197 template <int D = Dim, typename = std::enable_if_t<D == 1> >
196 template <int D = Dim, typename = std::enable_if_t<D == 1> >
198 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
197 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
199 {
198 {
200 }
199 }
201
200
202 /**
201 /**
203 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
202 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
204 * greater than 2 and must be a divisor of the total number of data in the vector
203 * greater than 2 and must be a divisor of the total number of data in the vector
205 * @param data the data the ArrayData will hold
204 * @param data the data the ArrayData will hold
206 * @param nbComponents the number of components
205 * @param nbComponents the number of components
207 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
206 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
208 * of the size of the data
207 * of the size of the data
209 */
208 */
210 template <int D = Dim, typename = std::enable_if_t<D == 2> >
209 template <int D = Dim, typename = std::enable_if_t<D == 2> >
211 explicit ArrayData(DataContainer data, int nbComponents)
210 explicit ArrayData(DataContainer data, int nbComponents)
212 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
211 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
213 {
212 {
214 if (nbComponents < 2) {
213 if (nbComponents < 2) {
215 throw std::invalid_argument{
214 throw std::invalid_argument{
216 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
215 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
217 .arg(nbComponents)
216 .arg(nbComponents)
218 .toStdString()};
217 .toStdString()};
219 }
218 }
220
219
221 if (m_Data.size() % m_NbComponents != 0) {
220 if (m_Data.size() % m_NbComponents != 0) {
222 throw std::invalid_argument{QString{
221 throw std::invalid_argument{QString{
223 "The number of components (%1) is inconsistent with the total number of data (%2)"}
222 "The number of components (%1) is inconsistent with the total number of data (%2)"}
224 .arg(m_Data.size(), nbComponents)
223 .arg(m_Data.size(), nbComponents)
225 .toStdString()};
224 .toStdString()};
226 }
225 }
227 }
226 }
228
227
229 /// Copy ctor
228 /// Copy ctor
230 explicit ArrayData(const ArrayData &other)
229 explicit ArrayData(const ArrayData &other)
231 {
230 {
232 QReadLocker otherLocker{&other.m_Lock};
231 QReadLocker otherLocker{&other.m_Lock};
233 m_Data = other.m_Data;
232 m_Data = other.m_Data;
234 m_NbComponents = other.m_NbComponents;
233 m_NbComponents = other.m_NbComponents;
235 }
234 }
236
235
237 // /////////////// //
236 // /////////////// //
238 // General methods //
237 // General methods //
239 // /////////////// //
238 // /////////////// //
240
239
241 /**
240 /**
242 * Merges into the array data an other array data. The two array datas must have the same number
241 * Merges into the array data an other array data. The two array datas must have the same number
243 * of components so the merge can be done
242 * of components so the merge can be done
244 * @param other the array data to merge with
243 * @param other the array data to merge with
245 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
244 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
246 * inserted at the end
245 * inserted at the end
247 */
246 */
248 void add(const ArrayData<Dim> &other, bool prepend = false)
247 void add(const ArrayData<Dim> &other, bool prepend = false)
249 {
248 {
250 QWriteLocker locker{&m_Lock};
249 QWriteLocker locker{&m_Lock};
251 QReadLocker otherLocker{&other.m_Lock};
250 QReadLocker otherLocker{&other.m_Lock};
252
251
253 if (m_NbComponents != other.componentCount()) {
252 if (m_NbComponents != other.componentCount()) {
254 return;
253 return;
255 }
254 }
256
255
257 insert(other.cbegin(), other.cend(), prepend);
256 insert(other.cbegin(), other.cend(), prepend);
258 }
257 }
259
258
260 void clear()
259 void clear()
261 {
260 {
262 QWriteLocker locker{&m_Lock};
261 QWriteLocker locker{&m_Lock};
263 m_Data.clear();
262 m_Data.clear();
264 }
263 }
265
264
266 int componentCount() const noexcept { return m_NbComponents; }
265 int componentCount() const noexcept { return m_NbComponents; }
267
266
268 /// @return the size (i.e. number of values) of a single component
267 /// @return the size (i.e. number of values) of a single component
269 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
268 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
270 int size() const
269 int size() const
271 {
270 {
272 QReadLocker locker{&m_Lock};
271 QReadLocker locker{&m_Lock};
273 return m_Data.size() / m_NbComponents;
272 return m_Data.size() / m_NbComponents;
274 }
273 }
275
274
276 /// @return the total size (i.e. number of values) of the array data
275 /// @return the total size (i.e. number of values) of the array data
277 int totalSize() const
276 int totalSize() const
278 {
277 {
279 QReadLocker locker{&m_Lock};
278 QReadLocker locker{&m_Lock};
280 return m_Data.size();
279 return m_Data.size();
281 }
280 }
282
281
283 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
282 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
284 {
283 {
285 QReadLocker locker{&m_Lock};
284 QReadLocker locker{&m_Lock};
286 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
285 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
287 }
286 }
288
287
289 // ///////// //
288 // ///////// //
290 // Iterators //
289 // Iterators //
291 // ///////// //
290 // ///////// //
292
291
293 ArrayDataIterator begin()
292 ArrayDataIterator begin()
294 {
293 {
295 return ArrayDataIterator{
294 return ArrayDataIterator{
296 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
295 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
297 m_Data, m_NbComponents, true)}};
296 m_Data, m_NbComponents, true)}};
298 }
297 }
299
298
300 ArrayDataIterator end()
299 ArrayDataIterator end()
301 {
300 {
302 return ArrayDataIterator{
301 return ArrayDataIterator{
303 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
302 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
304 m_Data, m_NbComponents, false)}};
303 m_Data, m_NbComponents, false)}};
305 }
304 }
306
305
307 ArrayDataIterator cbegin() const
306 ArrayDataIterator cbegin() const
308 {
307 {
309 return ArrayDataIterator{
308 return ArrayDataIterator{
310 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
309 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
311 m_Data, m_NbComponents, true)}};
310 m_Data, m_NbComponents, true)}};
312 }
311 }
313
312
314 ArrayDataIterator cend() const
313 ArrayDataIterator cend() const
315 {
314 {
316 return ArrayDataIterator{
315 return ArrayDataIterator{
317 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
316 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
318 m_Data, m_NbComponents, false)}};
317 m_Data, m_NbComponents, false)}};
319 }
318 }
320
319
321 void erase(ArrayDataIterator first, ArrayDataIterator last)
320 void erase(ArrayDataIterator first, ArrayDataIterator last)
322 {
321 {
323 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
322 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
324 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
323 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
325
324
326 if (firstImpl && lastImpl) {
325 if (firstImpl && lastImpl) {
327 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
326 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
328 }
327 }
329 }
328 }
330
329
331 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
330 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
332 {
331 {
333 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
332 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
334 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
333 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
335
334
336 if (firstImpl && lastImpl) {
335 if (firstImpl && lastImpl) {
337 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
336 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
338
337
339 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
338 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
340 }
339 }
341 }
340 }
342
341
343 /**
342 /**
344 * @return the data at a specified index
343 * @return the data at a specified index
345 * @remarks index must be a valid position
344 * @remarks index must be a valid position
346 */
345 */
347 double at(int index) const noexcept
346 double at(int index) const noexcept
348 {
347 {
349 QReadLocker locker{&m_Lock};
348 QReadLocker locker{&m_Lock};
350 return m_Data.at(index);
349 return m_Data.at(index);
351 }
350 }
352
351
353 // ///////////// //
352 // ///////////// //
354 // 1-dim methods //
353 // 1-dim methods //
355 // ///////////// //
354 // ///////////// //
356
355
357 /**
356 /**
358 * @return the data as a vector, as a const reference
357 * @return the data as a vector, as a const reference
359 * @remarks this method is only available for a unidimensional ArrayData
358 * @remarks this method is only available for a unidimensional ArrayData
360 */
359 */
361 template <int D = Dim, typename = std::enable_if_t<D == 1> >
360 template <int D = Dim, typename = std::enable_if_t<D == 1> >
362 DataContainer cdata() const noexcept
361 DataContainer cdata() const noexcept
363 {
362 {
364 return m_Data;
363 return m_Data;
365 }
364 }
366
365
367 private:
366 private:
368 DataContainer m_Data;
367 DataContainer m_Data;
369 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
368 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
370 int m_NbComponents;
369 int m_NbComponents;
371 mutable QReadWriteLock m_Lock;
370 mutable QReadWriteLock m_Lock;
372 };
371 };
373
372
374 #endif // SCIQLOP_ARRAYDATA_H
373 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,391 +1,391
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11
11
12 #include <QLoggingCategory>
12 #include <QLoggingCategory>
13 #include <QReadLocker>
13 #include <QReadLocker>
14 #include <QReadWriteLock>
14 #include <QReadWriteLock>
15 #include <memory>
15 #include <memory>
16
16
17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // definitions with inheritance. Inline method is used instead
18 // definitions with inheritance. Inline method is used instead
19 inline const QLoggingCategory &LOG_DataSeries()
19 inline const QLoggingCategory &LOG_DataSeries()
20 {
20 {
21 static const QLoggingCategory category{"DataSeries"};
21 static const QLoggingCategory category{"DataSeries"};
22 return category;
22 return category;
23 }
23 }
24
24
25 template <int Dim>
25 template <int Dim>
26 class DataSeries;
26 class DataSeries;
27
27
28 namespace dataseries_detail {
28 namespace dataseries_detail {
29
29
30 template <int Dim, bool IsConst>
30 template <int Dim, bool IsConst>
31 class IteratorValue : public DataSeriesIteratorValue::Impl {
31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 public:
32 public:
33 friend class DataSeries<Dim>;
33 friend class DataSeries<Dim>;
34
34
35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 {
39 {
40 }
40 }
41
41
42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 : dataSeries.valuesData()->cend())
46 : dataSeries.valuesData()->cend())
47 {
47 {
48 }
48 }
49
49
50 IteratorValue(const IteratorValue &other) = default;
50 IteratorValue(const IteratorValue &other) = default;
51
51
52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 {
53 {
54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 }
55 }
56
56
57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 return m_XIt->distance(*otherImpl.m_XIt);
59 return m_XIt->distance(*otherImpl.m_XIt);
60 }
60 }
61 catch (const std::bad_cast &) {
61 catch (const std::bad_cast &) {
62 return 0;
62 return 0;
63 }
63 }
64
64
65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 }
68 }
69 catch (const std::bad_cast &) {
69 catch (const std::bad_cast &) {
70 return false;
70 return false;
71 }
71 }
72
72
73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 return m_XIt->lowerThan(*otherImpl.m_XIt);
75 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 }
76 }
77 catch (const std::bad_cast &) {
77 catch (const std::bad_cast &) {
78 return false;
78 return false;
79 }
79 }
80
80
81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 {
82 {
83 auto result = clone();
83 auto result = clone();
84 result->next(offset);
84 result->next(offset);
85 return result;
85 return result;
86 }
86 }
87
87
88 void next(int offset) override
88 void next(int offset) override
89 {
89 {
90 m_XIt->next(offset);
90 m_XIt->next(offset);
91 m_ValuesIt->next(offset);
91 m_ValuesIt->next(offset);
92 }
92 }
93
93
94 void prev() override
94 void prev() override
95 {
95 {
96 --m_XIt;
96 --m_XIt;
97 --m_ValuesIt;
97 --m_ValuesIt;
98 }
98 }
99
99
100 double x() const override { return m_XIt->at(0); }
100 double x() const override { return m_XIt->at(0); }
101 double value() const override { return m_ValuesIt->at(0); }
101 double value() const override { return m_ValuesIt->at(0); }
102 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
102 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 double minValue() const override { return m_ValuesIt->min(); }
103 double minValue() const override { return m_ValuesIt->min(); }
104 double maxValue() const override { return m_ValuesIt->max(); }
104 double maxValue() const override { return m_ValuesIt->max(); }
105 QVector<double> values() const override { return m_ValuesIt->values(); }
105 QVector<double> values() const override { return m_ValuesIt->values(); }
106
106
107 void swap(DataSeriesIteratorValue::Impl &other) override
107 void swap(DataSeriesIteratorValue::Impl &other) override
108 {
108 {
109 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
109 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
110 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
111 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 }
112 }
113
113
114 private:
114 private:
115 ArrayDataIterator m_XIt;
115 ArrayDataIterator m_XIt;
116 ArrayDataIterator m_ValuesIt;
116 ArrayDataIterator m_ValuesIt;
117 };
117 };
118 } // namespace dataseries_detail
118 } // namespace dataseries_detail
119
119
120 /**
120 /**
121 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
121 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 *
122 *
123 * It proposes to set a dimension for the values ​​data.
123 * It proposes to set a dimension for the values ​​data.
124 *
124 *
125 * A DataSeries is always sorted on its x-axis data.
125 * A DataSeries is always sorted on its x-axis data.
126 *
126 *
127 * @tparam Dim The dimension of the values data
127 * @tparam Dim The dimension of the values data
128 *
128 *
129 */
129 */
130 template <int Dim>
130 template <int Dim>
131 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
131 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
132 friend class DataSeriesMergeHelper;
132 friend class DataSeriesMergeHelper;
133
133
134 public:
134 public:
135 /// @sa IDataSeries::xAxisData()
135 /// @sa IDataSeries::xAxisData()
136 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
136 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
137 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
137 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
138
138
139 /// @sa IDataSeries::xAxisUnit()
139 /// @sa IDataSeries::xAxisUnit()
140 Unit xAxisUnit() const override { return m_XAxisUnit; }
140 Unit xAxisUnit() const override { return m_XAxisUnit; }
141
141
142 /// @return the values dataset
142 /// @return the values dataset
143 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
143 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
144 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
144 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
145
145
146 /// @sa IDataSeries::valuesUnit()
146 /// @sa IDataSeries::valuesUnit()
147 Unit valuesUnit() const override { return m_ValuesUnit; }
147 Unit valuesUnit() const override { return m_ValuesUnit; }
148
148
149 int nbPoints() const override { return m_XAxisData->totalSize() + m_ValuesData->totalSize(); }
149 int nbPoints() const override { return m_ValuesData->totalSize(); }
150
150
151 void clear()
151 void clear()
152 {
152 {
153 m_XAxisData->clear();
153 m_XAxisData->clear();
154 m_ValuesData->clear();
154 m_ValuesData->clear();
155 }
155 }
156
156
157 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
157 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
158
158
159 /// Merges into the data series an other data series
159 /// Merges into the data series an other data series
160 /// @remarks the data series to merge with is cleared after the operation
160 /// @remarks the data series to merge with is cleared after the operation
161 void merge(IDataSeries *dataSeries) override
161 void merge(IDataSeries *dataSeries) override
162 {
162 {
163 dataSeries->lockWrite();
163 dataSeries->lockWrite();
164 lockWrite();
164 lockWrite();
165
165
166 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
166 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
167 DataSeriesMergeHelper::merge(*other, *this);
167 DataSeriesMergeHelper::merge(*other, *this);
168 }
168 }
169 else {
169 else {
170 qCWarning(LOG_DataSeries())
170 qCWarning(LOG_DataSeries())
171 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
171 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
172 }
172 }
173 unlock();
173 unlock();
174 dataSeries->unlock();
174 dataSeries->unlock();
175 }
175 }
176
176
177 void purge(double min, double max) override
177 void purge(double min, double max) override
178 {
178 {
179 // Nothing to purge if series is empty
179 // Nothing to purge if series is empty
180 if (isEmpty()) {
180 if (isEmpty()) {
181 return;
181 return;
182 }
182 }
183
183
184 if (min > max) {
184 if (min > max) {
185 std::swap(min, max);
185 std::swap(min, max);
186 }
186 }
187
187
188 // Nothing to purge if series min/max are inside purge range
188 // Nothing to purge if series min/max are inside purge range
189 auto xMin = cbegin()->x();
189 auto xMin = cbegin()->x();
190 auto xMax = (--cend())->x();
190 auto xMax = (--cend())->x();
191 if (xMin >= min && xMax <= max) {
191 if (xMin >= min && xMax <= max) {
192 return;
192 return;
193 }
193 }
194
194
195 auto lowerIt = std::lower_bound(
195 auto lowerIt = std::lower_bound(
196 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
196 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
197 erase(begin(), lowerIt);
197 erase(begin(), lowerIt);
198 auto upperIt = std::upper_bound(
198 auto upperIt = std::upper_bound(
199 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
199 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
200 erase(upperIt, end());
200 erase(upperIt, end());
201 }
201 }
202
202
203 // ///////// //
203 // ///////// //
204 // Iterators //
204 // Iterators //
205 // ///////// //
205 // ///////// //
206
206
207 DataSeriesIterator begin() override
207 DataSeriesIterator begin() override
208 {
208 {
209 return DataSeriesIterator{DataSeriesIteratorValue{
209 return DataSeriesIterator{DataSeriesIteratorValue{
210 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
210 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
211 }
211 }
212
212
213 DataSeriesIterator end() override
213 DataSeriesIterator end() override
214 {
214 {
215 return DataSeriesIterator{DataSeriesIteratorValue{
215 return DataSeriesIterator{DataSeriesIteratorValue{
216 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
216 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
217 }
217 }
218
218
219 DataSeriesIterator cbegin() const override
219 DataSeriesIterator cbegin() const override
220 {
220 {
221 return DataSeriesIterator{DataSeriesIteratorValue{
221 return DataSeriesIterator{DataSeriesIteratorValue{
222 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
222 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
223 }
223 }
224
224
225 DataSeriesIterator cend() const override
225 DataSeriesIterator cend() const override
226 {
226 {
227 return DataSeriesIterator{DataSeriesIteratorValue{
227 return DataSeriesIterator{DataSeriesIteratorValue{
228 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
228 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
229 }
229 }
230
230
231 void erase(DataSeriesIterator first, DataSeriesIterator last)
231 void erase(DataSeriesIterator first, DataSeriesIterator last)
232 {
232 {
233 auto firstImpl
233 auto firstImpl
234 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
234 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
235 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
235 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
236
236
237 if (firstImpl && lastImpl) {
237 if (firstImpl && lastImpl) {
238 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
238 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
239 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
239 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
240 }
240 }
241 }
241 }
242
242
243 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
243 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
244 {
244 {
245 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
245 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
246 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
246 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
247
247
248 if (firstImpl && lastImpl) {
248 if (firstImpl && lastImpl) {
249 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
249 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
250 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
250 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
251 }
251 }
252 }
252 }
253
253
254 /// @sa IDataSeries::minXAxisData()
254 /// @sa IDataSeries::minXAxisData()
255 DataSeriesIterator minXAxisData(double minXAxisData) const override
255 DataSeriesIterator minXAxisData(double minXAxisData) const override
256 {
256 {
257 return std::lower_bound(
257 return std::lower_bound(
258 cbegin(), cend(), minXAxisData,
258 cbegin(), cend(), minXAxisData,
259 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
259 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
260 }
260 }
261
261
262 /// @sa IDataSeries::maxXAxisData()
262 /// @sa IDataSeries::maxXAxisData()
263 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
263 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
264 {
264 {
265 // Gets the first element that greater than max value
265 // Gets the first element that greater than max value
266 auto it = std::upper_bound(
266 auto it = std::upper_bound(
267 cbegin(), cend(), maxXAxisData,
267 cbegin(), cend(), maxXAxisData,
268 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
268 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
269
269
270 return it == cbegin() ? cend() : --it;
270 return it == cbegin() ? cend() : --it;
271 }
271 }
272
272
273 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
273 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
274 double maxXAxisData) const override
274 double maxXAxisData) const override
275 {
275 {
276 if (minXAxisData > maxXAxisData) {
276 if (minXAxisData > maxXAxisData) {
277 std::swap(minXAxisData, maxXAxisData);
277 std::swap(minXAxisData, maxXAxisData);
278 }
278 }
279
279
280 auto begin = cbegin();
280 auto begin = cbegin();
281 auto end = cend();
281 auto end = cend();
282
282
283 auto lowerIt = std::lower_bound(
283 auto lowerIt = std::lower_bound(
284 begin, end, minXAxisData,
284 begin, end, minXAxisData,
285 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
285 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
286 auto upperIt = std::upper_bound(
286 auto upperIt = std::upper_bound(
287 lowerIt, end, maxXAxisData,
287 lowerIt, end, maxXAxisData,
288 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
288 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
289
289
290 return std::make_pair(lowerIt, upperIt);
290 return std::make_pair(lowerIt, upperIt);
291 }
291 }
292
292
293 std::pair<DataSeriesIterator, DataSeriesIterator>
293 std::pair<DataSeriesIterator, DataSeriesIterator>
294 valuesBounds(double minXAxisData, double maxXAxisData) const override
294 valuesBounds(double minXAxisData, double maxXAxisData) const override
295 {
295 {
296 // Places iterators to the correct x-axis range
296 // Places iterators to the correct x-axis range
297 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
297 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
298
298
299 // Returns end iterators if the range is empty
299 // Returns end iterators if the range is empty
300 if (xAxisRangeIts.first == xAxisRangeIts.second) {
300 if (xAxisRangeIts.first == xAxisRangeIts.second) {
301 return std::make_pair(cend(), cend());
301 return std::make_pair(cend(), cend());
302 }
302 }
303
303
304 // Gets the iterator on the min of all values data
304 // Gets the iterator on the min of all values data
305 auto minIt = std::min_element(
305 auto minIt = std::min_element(
306 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
306 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
307 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
307 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
308 });
308 });
309
309
310 // Gets the iterator on the max of all values data
310 // Gets the iterator on the max of all values data
311 auto maxIt = std::max_element(
311 auto maxIt = std::max_element(
312 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
312 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
313 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
313 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
314 });
314 });
315
315
316 return std::make_pair(minIt, maxIt);
316 return std::make_pair(minIt, maxIt);
317 }
317 }
318
318
319 // /////// //
319 // /////// //
320 // Mutexes //
320 // Mutexes //
321 // /////// //
321 // /////// //
322
322
323 virtual void lockRead() { m_Lock.lockForRead(); }
323 virtual void lockRead() { m_Lock.lockForRead(); }
324 virtual void lockWrite() { m_Lock.lockForWrite(); }
324 virtual void lockWrite() { m_Lock.lockForWrite(); }
325 virtual void unlock() { m_Lock.unlock(); }
325 virtual void unlock() { m_Lock.unlock(); }
326
326
327 protected:
327 protected:
328 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
328 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
329 /// DataSeries with no values will be created.
329 /// DataSeries with no values will be created.
330 /// @remarks data series is automatically sorted on its x-axis data
330 /// @remarks data series is automatically sorted on its x-axis data
331 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
331 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
332 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
332 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
333 : m_XAxisData{xAxisData},
333 : m_XAxisData{xAxisData},
334 m_XAxisUnit{xAxisUnit},
334 m_XAxisUnit{xAxisUnit},
335 m_ValuesData{valuesData},
335 m_ValuesData{valuesData},
336 m_ValuesUnit{valuesUnit}
336 m_ValuesUnit{valuesUnit}
337 {
337 {
338 if (m_XAxisData->size() != m_ValuesData->size()) {
338 if (m_XAxisData->size() != m_ValuesData->size()) {
339 clear();
339 clear();
340 }
340 }
341
341
342 // Sorts data if it's not the case
342 // Sorts data if it's not the case
343 const auto &xAxisCData = m_XAxisData->cdata();
343 const auto &xAxisCData = m_XAxisData->cdata();
344 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
344 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
345 sort();
345 sort();
346 }
346 }
347 }
347 }
348
348
349 /// Copy ctor
349 /// Copy ctor
350 explicit DataSeries(const DataSeries<Dim> &other)
350 explicit DataSeries(const DataSeries<Dim> &other)
351 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
351 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
352 m_XAxisUnit{other.m_XAxisUnit},
352 m_XAxisUnit{other.m_XAxisUnit},
353 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
353 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
354 m_ValuesUnit{other.m_ValuesUnit}
354 m_ValuesUnit{other.m_ValuesUnit}
355 {
355 {
356 // Since a series is ordered from its construction and is always ordered, it is not
356 // Since a series is ordered from its construction and is always ordered, it is not
357 // necessary to call the sort method here ('other' is sorted)
357 // necessary to call the sort method here ('other' is sorted)
358 }
358 }
359
359
360 /// Assignment operator
360 /// Assignment operator
361 template <int D>
361 template <int D>
362 DataSeries &operator=(DataSeries<D> other)
362 DataSeries &operator=(DataSeries<D> other)
363 {
363 {
364 std::swap(m_XAxisData, other.m_XAxisData);
364 std::swap(m_XAxisData, other.m_XAxisData);
365 std::swap(m_XAxisUnit, other.m_XAxisUnit);
365 std::swap(m_XAxisUnit, other.m_XAxisUnit);
366 std::swap(m_ValuesData, other.m_ValuesData);
366 std::swap(m_ValuesData, other.m_ValuesData);
367 std::swap(m_ValuesUnit, other.m_ValuesUnit);
367 std::swap(m_ValuesUnit, other.m_ValuesUnit);
368
368
369 return *this;
369 return *this;
370 }
370 }
371
371
372 private:
372 private:
373 /**
373 /**
374 * Sorts data series on its x-axis data
374 * Sorts data series on its x-axis data
375 */
375 */
376 void sort() noexcept
376 void sort() noexcept
377 {
377 {
378 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
378 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
379 m_XAxisData = m_XAxisData->sort(permutation);
379 m_XAxisData = m_XAxisData->sort(permutation);
380 m_ValuesData = m_ValuesData->sort(permutation);
380 m_ValuesData = m_ValuesData->sort(permutation);
381 }
381 }
382
382
383 std::shared_ptr<ArrayData<1> > m_XAxisData;
383 std::shared_ptr<ArrayData<1> > m_XAxisData;
384 Unit m_XAxisUnit;
384 Unit m_XAxisUnit;
385 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
385 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
386 Unit m_ValuesUnit;
386 Unit m_ValuesUnit;
387
387
388 QReadWriteLock m_Lock;
388 QReadWriteLock m_Lock;
389 };
389 };
390
390
391 #endif // SCIQLOP_DATASERIES_H
391 #endif // SCIQLOP_DATASERIES_H
@@ -1,111 +1,96
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
4 #include <Common/MetaTypes.h>
4 #include <Common/MetaTypes.h>
5 #include <Data/DataSeriesIterator.h>
5 #include <Data/DataSeriesIterator.h>
6 #include <Data/SqpRange.h>
6 #include <Data/SqpRange.h>
7 #include <Data/Unit.h>
7
8
8 #include <memory>
9 #include <memory>
9
10
10 #include <QString>
11 #include <QString>
11
12
12 template <int Dim>
13 template <int Dim>
13 class ArrayData;
14 class ArrayData;
14
15
15 struct Unit {
16 explicit Unit(const QString &name = {}, bool timeUnit = false)
17 : m_Name{name}, m_TimeUnit{timeUnit}
18 {
19 }
20
21 inline bool operator==(const Unit &other) const
22 {
23 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
24 }
25 inline bool operator!=(const Unit &other) const { return !(*this == other); }
26
27 QString m_Name; ///< Unit name
28 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
29 };
30
31 /**
16 /**
32 * @brief The IDataSeries aims to declare a data series.
17 * @brief The IDataSeries aims to declare a data series.
33 *
18 *
34 * A data series is an entity that contains at least :
19 * A data series is an entity that contains at least :
35 * - one dataset representing the x-axis
20 * - one dataset representing the x-axis
36 * - one dataset representing the values
21 * - one dataset representing the values
37 *
22 *
38 * Each dataset is represented by an ArrayData, and is associated with a unit.
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
39 *
24 *
40 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
41 * IDataSeries. The x-axis dataset is always unidimensional.
26 * IDataSeries. The x-axis dataset is always unidimensional.
42 *
27 *
43 * @sa ArrayData
28 * @sa ArrayData
44 */
29 */
45 class IDataSeries {
30 class IDataSeries {
46 public:
31 public:
47 virtual ~IDataSeries() noexcept = default;
32 virtual ~IDataSeries() noexcept = default;
48
33
49 /// Returns the x-axis dataset
34 /// Returns the x-axis dataset
50 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
51
36
52 /// Returns the x-axis dataset (as const)
37 /// Returns the x-axis dataset (as const)
53 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
54
39
55 virtual Unit xAxisUnit() const = 0;
40 virtual Unit xAxisUnit() const = 0;
56
41
57 virtual Unit valuesUnit() const = 0;
42 virtual Unit valuesUnit() const = 0;
58
43
59 virtual void merge(IDataSeries *dataSeries) = 0;
44 virtual void merge(IDataSeries *dataSeries) = 0;
60 /// Removes from data series all entries whose value on the x-axis is not between min and max
45 /// Removes from data series all entries whose value on the x-axis is not between min and max
61 virtual void purge(double min, double max) = 0;
46 virtual void purge(double min, double max) = 0;
62
47
63 /// @todo Review the name and signature of this method
48 /// @todo Review the name and signature of this method
64 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
49 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
65
50
66 virtual std::unique_ptr<IDataSeries> clone() const = 0;
51 virtual std::unique_ptr<IDataSeries> clone() const = 0;
67
52
68 /// @return the total number of points contained in the data series
53 /// @return the total number of points contained in the data series
69 virtual int nbPoints() const = 0;
54 virtual int nbPoints() const = 0;
70
55
71 // ///////// //
56 // ///////// //
72 // Iterators //
57 // Iterators //
73 // ///////// //
58 // ///////// //
74
59
75 virtual DataSeriesIterator cbegin() const = 0;
60 virtual DataSeriesIterator cbegin() const = 0;
76 virtual DataSeriesIterator cend() const = 0;
61 virtual DataSeriesIterator cend() const = 0;
77 virtual DataSeriesIterator begin() = 0;
62 virtual DataSeriesIterator begin() = 0;
78 virtual DataSeriesIterator end() = 0;
63 virtual DataSeriesIterator end() = 0;
79
64
80 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
65 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
81 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
66 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
82 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
67 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
83
68
84 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
69 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
85 /// equal to the value passed in parameter, or the end iterator if there is no matching value
70 /// equal to the value passed in parameter, or the end iterator if there is no matching value
86 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
71 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
87
72
88 /// @return the iterators pointing to the range of data whose x-axis values are between min and
73 /// @return the iterators pointing to the range of data whose x-axis values are between min and
89 /// max passed in parameters
74 /// max passed in parameters
90 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
75 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
91 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
76 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
92
77
93 /// @return two iterators pointing to the data that have respectively the min and the max value
78 /// @return two iterators pointing to the data that have respectively the min and the max value
94 /// data of a data series' range. The search is performed for a given x-axis range.
79 /// data of a data series' range. The search is performed for a given x-axis range.
95 /// @sa xAxisRange()
80 /// @sa xAxisRange()
96 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
97 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
82 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
98
83
99 // /////// //
84 // /////// //
100 // Mutexes //
85 // Mutexes //
101 // /////// //
86 // /////// //
102
87
103 virtual void lockRead() = 0;
88 virtual void lockRead() = 0;
104 virtual void lockWrite() = 0;
89 virtual void lockWrite() = 0;
105 virtual void unlock() = 0;
90 virtual void unlock() = 0;
106 };
91 };
107
92
108 // Required for using shared_ptr in signals/slots
93 // Required for using shared_ptr in signals/slots
109 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
94 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
110
95
111 #endif // SCIQLOP_IDATASERIES_H
96 #endif // SCIQLOP_IDATASERIES_H
@@ -1,409 +1,409
1 #include <Variable/Variable.h>
1 #include <Variable/Variable.h>
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4
4
5 #include <QObject>
5 #include <QObject>
6 #include <QtTest>
6 #include <QtTest>
7
7
8 #include <memory>
8 #include <memory>
9
9
10 namespace {
10 namespace {
11
11
12 /// Generates a date in double
12 /// Generates a date in double
13 auto date = [](int year, int month, int day, int hours, int minutes, int seconds) {
13 auto date = [](int year, int month, int day, int hours, int minutes, int seconds) {
14 return DateUtils::secondsSinceEpoch(
14 return DateUtils::secondsSinceEpoch(
15 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
15 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
16 };
16 };
17
17
18 /// Generates a series of test data for a range
18 /// Generates a series of test data for a range
19 std::shared_ptr<ScalarSeries> dataSeries(const SqpRange &range)
19 std::shared_ptr<ScalarSeries> dataSeries(const SqpRange &range)
20 {
20 {
21 auto xAxisData = std::vector<double>{};
21 auto xAxisData = std::vector<double>{};
22 auto valuesData = std::vector<double>{};
22 auto valuesData = std::vector<double>{};
23
23
24 auto value = 0;
24 auto value = 0;
25 for (auto x = range.m_TStart; x <= range.m_TEnd; ++x, ++value) {
25 for (auto x = range.m_TStart; x <= range.m_TEnd; ++x, ++value) {
26 xAxisData.push_back(x);
26 xAxisData.push_back(x);
27 valuesData.push_back(value);
27 valuesData.push_back(value);
28 }
28 }
29
29
30 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
30 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
31 Unit{});
31 Unit{});
32 }
32 }
33
33
34 } // namespace
34 } // namespace
35
35
36 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
36 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
37
37
38 class TestVariable : public QObject {
38 class TestVariable : public QObject {
39 Q_OBJECT
39 Q_OBJECT
40
40
41 private slots:
41 private slots:
42 void testClone_data();
42 void testClone_data();
43 void testClone();
43 void testClone();
44
44
45 void testNotInCacheRangeList();
45 void testNotInCacheRangeList();
46 void testInCacheRangeList();
46 void testInCacheRangeList();
47
47
48 void testNbPoints_data();
48 void testNbPoints_data();
49 void testNbPoints();
49 void testNbPoints();
50
50
51 void testRealRange_data();
51 void testRealRange_data();
52 void testRealRange();
52 void testRealRange();
53 };
53 };
54
54
55 void TestVariable::testClone_data()
55 void TestVariable::testClone_data()
56 {
56 {
57 // ////////////// //
57 // ////////////// //
58 // Test structure //
58 // Test structure //
59 // ////////////// //
59 // ////////////// //
60
60
61 QTest::addColumn<QString>("name");
61 QTest::addColumn<QString>("name");
62 QTest::addColumn<QVariantHash>("metadata");
62 QTest::addColumn<QVariantHash>("metadata");
63 QTest::addColumn<SqpRange>("range");
63 QTest::addColumn<SqpRange>("range");
64 QTest::addColumn<SqpRange>("cacheRange");
64 QTest::addColumn<SqpRange>("cacheRange");
65 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
65 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
66
66
67 // ////////// //
67 // ////////// //
68 // Test cases //
68 // Test cases //
69 // ////////// //
69 // ////////// //
70
70
71 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
71 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
72 QTest::newRow("clone1") << QStringLiteral("var1")
72 QTest::newRow("clone1") << QStringLiteral("var1")
73 << QVariantHash{{"data1", 1}, {"data2", "abc"}}
73 << QVariantHash{{"data1", 1}, {"data2", "abc"}}
74 << SqpRange{date(2017, 1, 1, 12, 30, 0), (date(2017, 1, 1, 12, 45, 0))}
74 << SqpRange{date(2017, 1, 1, 12, 30, 0), (date(2017, 1, 1, 12, 45, 0))}
75 << cacheRange << dataSeries(cacheRange);
75 << cacheRange << dataSeries(cacheRange);
76 }
76 }
77
77
78 void TestVariable::testClone()
78 void TestVariable::testClone()
79 {
79 {
80 // Creates variable
80 // Creates variable
81 QFETCH(QString, name);
81 QFETCH(QString, name);
82 QFETCH(QVariantHash, metadata);
82 QFETCH(QVariantHash, metadata);
83 QFETCH(SqpRange, range);
83 QFETCH(SqpRange, range);
84 QFETCH(SqpRange, cacheRange);
84 QFETCH(SqpRange, cacheRange);
85 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
85 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
86
86
87 Variable variable{name, metadata};
87 Variable variable{name, metadata};
88 variable.setRange(range);
88 variable.setRange(range);
89 variable.setCacheRange(cacheRange);
89 variable.setCacheRange(cacheRange);
90 variable.mergeDataSeries(dataSeries);
90 variable.mergeDataSeries(dataSeries);
91
91
92 // Clones variable
92 // Clones variable
93 auto clone = variable.clone();
93 auto clone = variable.clone();
94
94
95 // Checks cloned variable's state
95 // Checks cloned variable's state
96 QCOMPARE(clone->name(), name);
96 QCOMPARE(clone->name(), name);
97 QCOMPARE(clone->metadata(), metadata);
97 QCOMPARE(clone->metadata(), metadata);
98 QCOMPARE(clone->range(), range);
98 QCOMPARE(clone->range(), range);
99 QCOMPARE(clone->cacheRange(), cacheRange);
99 QCOMPARE(clone->cacheRange(), cacheRange);
100
100
101 // Compares data series
101 // Compares data series
102 if (dataSeries != nullptr) {
102 if (dataSeries != nullptr) {
103 QVERIFY(clone->dataSeries() != nullptr);
103 QVERIFY(clone->dataSeries() != nullptr);
104 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), clone->dataSeries()->cbegin(),
104 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), clone->dataSeries()->cbegin(),
105 clone->dataSeries()->cend(), [](const auto &it1, const auto &it2) {
105 clone->dataSeries()->cend(), [](const auto &it1, const auto &it2) {
106 return it1.x() == it2.x() && it1.value() == it2.value();
106 return it1.x() == it2.x() && it1.value() == it2.value();
107 }));
107 }));
108 }
108 }
109 else {
109 else {
110 QVERIFY(clone->dataSeries() == nullptr);
110 QVERIFY(clone->dataSeries() == nullptr);
111 }
111 }
112 }
112 }
113
113
114 void TestVariable::testNotInCacheRangeList()
114 void TestVariable::testNotInCacheRangeList()
115 {
115 {
116 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
116 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
117 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
117 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
118
118
119 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
119 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
120
120
121 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
121 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
122 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
122 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
123
123
124 auto sqpCR
124 auto sqpCR
125 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
125 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
126
126
127 Variable var{"Var test"};
127 Variable var{"Var test"};
128 var.setRange(sqpR);
128 var.setRange(sqpR);
129 var.setCacheRange(sqpCR);
129 var.setCacheRange(sqpCR);
130
130
131 // 1: [ts,te] < varTS
131 // 1: [ts,te] < varTS
132 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
132 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
133 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
133 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
134 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
134 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
135
135
136 auto notInCach = var.provideNotInCacheRangeList(sqp);
136 auto notInCach = var.provideNotInCacheRangeList(sqp);
137
137
138 QCOMPARE(notInCach.size(), 1);
138 QCOMPARE(notInCach.size(), 1);
139
139
140 auto notInCachRange = notInCach.first();
140 auto notInCachRange = notInCach.first();
141
141
142 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
142 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
143 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
143 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
144
144
145 // 2: ts < varTS < te < varTE
145 // 2: ts < varTS < te < varTE
146 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
146 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
147 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
147 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
148 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
148 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
149 notInCach = var.provideNotInCacheRangeList(sqp);
149 notInCach = var.provideNotInCacheRangeList(sqp);
150 QCOMPARE(notInCach.size(), 1);
150 QCOMPARE(notInCach.size(), 1);
151 notInCachRange = notInCach.first();
151 notInCachRange = notInCach.first();
152 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
152 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
153 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
153 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
154
154
155 // 3: varTS < ts < te < varTE
155 // 3: varTS < ts < te < varTE
156 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
156 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
157 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
157 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
158 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
158 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
159 notInCach = var.provideNotInCacheRangeList(sqp);
159 notInCach = var.provideNotInCacheRangeList(sqp);
160 QCOMPARE(notInCach.size(), 0);
160 QCOMPARE(notInCach.size(), 0);
161
161
162
162
163 // 4: varTS < ts < varTE < te
163 // 4: varTS < ts < varTE < te
164 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
164 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
165 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
165 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
166 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
166 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
167 notInCach = var.provideNotInCacheRangeList(sqp);
167 notInCach = var.provideNotInCacheRangeList(sqp);
168 QCOMPARE(notInCach.size(), 1);
168 QCOMPARE(notInCach.size(), 1);
169 notInCachRange = notInCach.first();
169 notInCachRange = notInCach.first();
170 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
170 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
171 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
171 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
172
172
173 // 5: varTS < varTE < ts < te
173 // 5: varTS < varTE < ts < te
174 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
174 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
175 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
175 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
176 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
176 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
177 notInCach = var.provideNotInCacheRangeList(sqp);
177 notInCach = var.provideNotInCacheRangeList(sqp);
178 QCOMPARE(notInCach.size(), 1);
178 QCOMPARE(notInCach.size(), 1);
179 notInCachRange = notInCach.first();
179 notInCachRange = notInCach.first();
180 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
180 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
181 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
181 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
182
182
183 // 6: ts <varTS < varTE < te
183 // 6: ts <varTS < varTE < te
184 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
184 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
185 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
185 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
186 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
186 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
187 notInCach = var.provideNotInCacheRangeList(sqp);
187 notInCach = var.provideNotInCacheRangeList(sqp);
188 QCOMPARE(notInCach.size(), 2);
188 QCOMPARE(notInCach.size(), 2);
189 notInCachRange = notInCach.first();
189 notInCachRange = notInCach.first();
190 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
190 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
191 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
191 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
192 notInCachRange = notInCach[1];
192 notInCachRange = notInCach[1];
193 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
193 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
194 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
194 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
195 }
195 }
196
196
197
197
198 void TestVariable::testInCacheRangeList()
198 void TestVariable::testInCacheRangeList()
199 {
199 {
200 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
200 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
201 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
201 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
202
202
203 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
203 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
204
204
205 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
205 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
206 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
206 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
207 auto sqpCR
207 auto sqpCR
208 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
208 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
209
209
210 Variable var{"Var test"};
210 Variable var{"Var test"};
211 var.setRange(sqpR);
211 var.setRange(sqpR);
212 var.setCacheRange(sqpCR);
212 var.setCacheRange(sqpCR);
213
213
214 // 1: [ts,te] < varTS
214 // 1: [ts,te] < varTS
215 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
215 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
216 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
216 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
217 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
217 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
218
218
219 auto notInCach = var.provideInCacheRangeList(sqp);
219 auto notInCach = var.provideInCacheRangeList(sqp);
220
220
221 QCOMPARE(notInCach.size(), 0);
221 QCOMPARE(notInCach.size(), 0);
222
222
223 // 2: ts < varTS < te < varTE
223 // 2: ts < varTS < te < varTE
224 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
224 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
225 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
225 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
226 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
226 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
227 notInCach = var.provideInCacheRangeList(sqp);
227 notInCach = var.provideInCacheRangeList(sqp);
228 QCOMPARE(notInCach.size(), 1);
228 QCOMPARE(notInCach.size(), 1);
229 auto notInCachRange = notInCach.first();
229 auto notInCachRange = notInCach.first();
230 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
230 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
231 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
231 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
232
232
233 // 3: varTS < ts < te < varTE
233 // 3: varTS < ts < te < varTE
234 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
234 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
235 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
235 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
236 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
236 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
237 notInCach = var.provideInCacheRangeList(sqp);
237 notInCach = var.provideInCacheRangeList(sqp);
238 QCOMPARE(notInCach.size(), 1);
238 QCOMPARE(notInCach.size(), 1);
239 notInCachRange = notInCach.first();
239 notInCachRange = notInCach.first();
240 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
240 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
241 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
241 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
242
242
243 // 4: varTS < ts < varTE < te
243 // 4: varTS < ts < varTE < te
244 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
244 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
245 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
245 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
246 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
246 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
247 notInCach = var.provideInCacheRangeList(sqp);
247 notInCach = var.provideInCacheRangeList(sqp);
248 QCOMPARE(notInCach.size(), 1);
248 QCOMPARE(notInCach.size(), 1);
249 notInCachRange = notInCach.first();
249 notInCachRange = notInCach.first();
250 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
250 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
251 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
251 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
252
252
253 // 5: varTS < varTE < ts < te
253 // 5: varTS < varTE < ts < te
254 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
254 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
255 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
255 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
256 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
256 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
257 notInCach = var.provideInCacheRangeList(sqp);
257 notInCach = var.provideInCacheRangeList(sqp);
258 QCOMPARE(notInCach.size(), 0);
258 QCOMPARE(notInCach.size(), 0);
259
259
260 // 6: ts <varTS < varTE < te
260 // 6: ts <varTS < varTE < te
261 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
261 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
262 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
262 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
263 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
263 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
264 notInCach = var.provideInCacheRangeList(sqp);
264 notInCach = var.provideInCacheRangeList(sqp);
265 QCOMPARE(notInCach.size(), 1);
265 QCOMPARE(notInCach.size(), 1);
266 notInCachRange = notInCach.first();
266 notInCachRange = notInCach.first();
267 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
267 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
268 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
268 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
269 }
269 }
270
270
271 namespace {
271 namespace {
272
272
273 /// Struct used to represent an operation for @sa TestVariable::testNbPoints()
273 /// Struct used to represent an operation for @sa TestVariable::testNbPoints()
274 struct NbPointsOperation {
274 struct NbPointsOperation {
275 SqpRange m_CacheRange; /// Range to set for the variable
275 SqpRange m_CacheRange; /// Range to set for the variable
276 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
276 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
277 int m_ExpectedNbPoints; /// Number of points in the variable expected after operation
277 int m_ExpectedNbPoints; /// Number of points in the variable expected after operation
278 };
278 };
279
279
280 using NbPointsOperations = std::vector<NbPointsOperation>;
280 using NbPointsOperations = std::vector<NbPointsOperation>;
281
281
282 } // namespace
282 } // namespace
283
283
284 Q_DECLARE_METATYPE(NbPointsOperations)
284 Q_DECLARE_METATYPE(NbPointsOperations)
285
285
286 void TestVariable::testNbPoints_data()
286 void TestVariable::testNbPoints_data()
287 {
287 {
288 // ////////////// //
288 // ////////////// //
289 // Test structure //
289 // Test structure //
290 // ////////////// //
290 // ////////////// //
291
291
292 QTest::addColumn<NbPointsOperations>("operations");
292 QTest::addColumn<NbPointsOperations>("operations");
293
293
294 // ////////// //
294 // ////////// //
295 // Test cases //
295 // Test cases //
296 // ////////// //
296 // ////////// //
297 NbPointsOperations operations{};
297 NbPointsOperations operations{};
298
298
299 // Sets cache range (expected nb points = series xAxis data + series values data)
299 // Sets cache range (expected nb points = values data)
300 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 9)};
300 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 9)};
301 operations.push_back({cacheRange, dataSeries(cacheRange), 20});
301 operations.push_back({cacheRange, dataSeries(cacheRange), 10});
302
302
303 // Doubles cache but don't add data series (expected nb points don't change)
303 // Doubles cache but don't add data series (expected nb points don't change)
304 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
304 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
305 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 20});
305 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 10});
306
306
307 // Doubles cache and data series (expected nb points change)
307 // Doubles cache and data series (expected nb points change)
308 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
308 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
309 operations.push_back({cacheRange, dataSeries(cacheRange), 40});
309 operations.push_back({cacheRange, dataSeries(cacheRange), 20});
310
310
311 // Decreases cache (expected nb points decreases as the series is purged)
311 // Decreases cache (expected nb points decreases as the series is purged)
312 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 5), date(2017, 1, 1, 12, 0, 9)};
312 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 5), date(2017, 1, 1, 12, 0, 9)};
313 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 10});
313 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 5});
314
314
315 QTest::newRow("nbPoints1") << operations;
315 QTest::newRow("nbPoints1") << operations;
316 }
316 }
317
317
318 void TestVariable::testNbPoints()
318 void TestVariable::testNbPoints()
319 {
319 {
320 // Creates variable
320 // Creates variable
321 Variable variable{"var"};
321 Variable variable{"var"};
322 QCOMPARE(variable.nbPoints(), 0);
322 QCOMPARE(variable.nbPoints(), 0);
323
323
324 QFETCH(NbPointsOperations, operations);
324 QFETCH(NbPointsOperations, operations);
325 for (const auto &operation : operations) {
325 for (const auto &operation : operations) {
326 // Sets cache range and merge data series
326 // Sets cache range and merge data series
327 variable.setCacheRange(operation.m_CacheRange);
327 variable.setCacheRange(operation.m_CacheRange);
328 if (operation.m_DataSeries != nullptr) {
328 if (operation.m_DataSeries != nullptr) {
329 variable.mergeDataSeries(operation.m_DataSeries);
329 variable.mergeDataSeries(operation.m_DataSeries);
330 }
330 }
331
331
332 // Checks nb points
332 // Checks nb points
333 QCOMPARE(variable.nbPoints(), operation.m_ExpectedNbPoints);
333 QCOMPARE(variable.nbPoints(), operation.m_ExpectedNbPoints);
334 }
334 }
335 }
335 }
336
336
337 namespace {
337 namespace {
338
338
339 /// Struct used to represent a range operation on a variable
339 /// Struct used to represent a range operation on a variable
340 /// @sa TestVariable::testRealRange()
340 /// @sa TestVariable::testRealRange()
341 struct RangeOperation {
341 struct RangeOperation {
342 SqpRange m_CacheRange; /// Range to set for the variable
342 SqpRange m_CacheRange; /// Range to set for the variable
343 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
343 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
344 SqpRange m_ExpectedRealRange; /// Real Range expected after operation on the variable
344 SqpRange m_ExpectedRealRange; /// Real Range expected after operation on the variable
345 };
345 };
346
346
347 using RangeOperations = std::vector<RangeOperation>;
347 using RangeOperations = std::vector<RangeOperation>;
348
348
349 } // namespace
349 } // namespace
350
350
351 Q_DECLARE_METATYPE(RangeOperations)
351 Q_DECLARE_METATYPE(RangeOperations)
352
352
353 void TestVariable::testRealRange_data()
353 void TestVariable::testRealRange_data()
354 {
354 {
355 // ////////////// //
355 // ////////////// //
356 // Test structure //
356 // Test structure //
357 // ////////////// //
357 // ////////////// //
358
358
359 QTest::addColumn<RangeOperations>("operations");
359 QTest::addColumn<RangeOperations>("operations");
360
360
361 // ////////// //
361 // ////////// //
362 // Test cases //
362 // Test cases //
363 // ////////// //
363 // ////////// //
364 RangeOperations operations{};
364 RangeOperations operations{};
365
365
366 // Inits cache range and data series (expected real range = cache range)
366 // Inits cache range and data series (expected real range = cache range)
367 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
367 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
368 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
368 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
369
369
370 // Changes cache range and updates data series (expected real range = cache range)
370 // Changes cache range and updates data series (expected real range = cache range)
371 cacheRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
371 cacheRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
372 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
372 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
373
373
374 // Changes cache range and update data series but with a lower range (expected real range =
374 // Changes cache range and update data series but with a lower range (expected real range =
375 // data series range)
375 // data series range)
376 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 16, 0, 0)};
376 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 16, 0, 0)};
377 auto dataSeriesRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
377 auto dataSeriesRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
378 operations.push_back({cacheRange, dataSeries(dataSeriesRange), dataSeriesRange});
378 operations.push_back({cacheRange, dataSeries(dataSeriesRange), dataSeriesRange});
379
379
380 // Changes cache range but DON'T update data series (expected real range = cache range
380 // Changes cache range but DON'T update data series (expected real range = cache range
381 // before operation)
381 // before operation)
382 cacheRange = SqpRange{date(2017, 1, 1, 10, 0, 0), date(2017, 1, 1, 17, 0, 0)};
382 cacheRange = SqpRange{date(2017, 1, 1, 10, 0, 0), date(2017, 1, 1, 17, 0, 0)};
383 operations.push_back({cacheRange, nullptr, dataSeriesRange});
383 operations.push_back({cacheRange, nullptr, dataSeriesRange});
384
384
385 QTest::newRow("realRange1") << operations;
385 QTest::newRow("realRange1") << operations;
386 }
386 }
387
387
388 void TestVariable::testRealRange()
388 void TestVariable::testRealRange()
389 {
389 {
390 // Creates variable (real range is invalid)
390 // Creates variable (real range is invalid)
391 Variable variable{"var"};
391 Variable variable{"var"};
392 QCOMPARE(variable.realRange(), INVALID_RANGE);
392 QCOMPARE(variable.realRange(), INVALID_RANGE);
393
393
394 QFETCH(RangeOperations, operations);
394 QFETCH(RangeOperations, operations);
395 for (const auto &operation : operations) {
395 for (const auto &operation : operations) {
396 // Sets cache range and merge data series
396 // Sets cache range and merge data series
397 variable.setCacheRange(operation.m_CacheRange);
397 variable.setCacheRange(operation.m_CacheRange);
398 if (operation.m_DataSeries != nullptr) {
398 if (operation.m_DataSeries != nullptr) {
399 variable.mergeDataSeries(operation.m_DataSeries);
399 variable.mergeDataSeries(operation.m_DataSeries);
400 }
400 }
401
401
402 // Checks real range
402 // Checks real range
403 QCOMPARE(variable.realRange(), operation.m_ExpectedRealRange);
403 QCOMPARE(variable.realRange(), operation.m_ExpectedRealRange);
404 }
404 }
405 }
405 }
406
406
407
407
408 QTEST_MAIN(TestVariable)
408 QTEST_MAIN(TestVariable)
409 #include "TestVariable.moc"
409 #include "TestVariable.moc"
General Comments 0
You need to be logged in to leave comments. Login now