##// END OF EJS Templates
Merge branch 'feature/ImprovePerfs3' into develop
Alexandre Leroux -
r648:cdcfd45a9b91 merge
parent child
Show More
@@ -1,139 +1,139
1 #ifndef SCIQLOP_SORTUTILS_H
1 #ifndef SCIQLOP_SORTUTILS_H
2 #define SCIQLOP_SORTUTILS_H
2 #define SCIQLOP_SORTUTILS_H
3
3
4 #include <algorithm>
4 #include <algorithm>
5 #include <cmath>
5 #include <cmath>
6 #include <numeric>
6 #include <numeric>
7 #include <vector>
7 #include <vector>
8
8
9 /**
9 /**
10 * Utility class with methods for sorting data
10 * Utility class with methods for sorting data
11 */
11 */
12 struct SortUtils {
12 struct SortUtils {
13 /**
13 /**
14 * Generates a vector representing the index of insertion of each data of a container if this
14 * Generates a vector representing the index of insertion of each data of a container if this
15 * one had to be sorted according to a comparison function.
15 * one had to be sorted according to a comparison function.
16 *
16 *
17 * For example:
17 * For example:
18 * If the container is a vector {1; 4; 2; 5; 3} and the comparison function is std::less, the
18 * If the container is a vector {1; 4; 2; 5; 3} and the comparison function is std::less, the
19 * result would be : {0; 3; 1; 4; 2}
19 * result would be : {0; 3; 1; 4; 2}
20 *
20 *
21 * @tparam Container the type of the container.
21 * @tparam Container the type of the container.
22 * @tparam Compare the type of the comparison function
22 * @tparam Compare the type of the comparison function
23 * @param container the container from which to generate the result. The container must have a
23 * @param container the container from which to generate the result. The container must have a
24 * at() method that returns a value associated to an index
24 * at() method that returns a value associated to an index
25 * @param compare the comparison function
25 * @param compare the comparison function
26 */
26 */
27 template <typename Container, typename Compare>
27 template <typename Container, typename Compare>
28 static std::vector<int> sortPermutation(const Container &container, const Compare &compare)
28 static std::vector<int> sortPermutation(const Container &container, const Compare &compare)
29 {
29 {
30 auto permutation = std::vector<int>{};
30 auto permutation = std::vector<int>{};
31 permutation.resize(container.size());
31 permutation.resize(container.size());
32
32
33 std::iota(permutation.begin(), permutation.end(), 0);
33 std::iota(permutation.begin(), permutation.end(), 0);
34 std::sort(permutation.begin(), permutation.end(),
34 std::sort(permutation.begin(), permutation.end(),
35 [&](int i, int j) { return compare(container.at(i), container.at(j)); });
35 [&](int i, int j) { return compare(container.at(i), container.at(j)); });
36 return permutation;
36 return permutation;
37 }
37 }
38
38
39 /**
39 /**
40 * Sorts a container according to indices passed in parameter. The number of data in the
40 * Sorts a container according to indices passed in parameter. The number of data in the
41 * container must be a multiple of the number of indices used to sort the container.
41 * container must be a multiple of the number of indices used to sort the container.
42 *
42 *
43 * Example 1:
43 * Example 1:
44 * container: {1, 2, 3, 4, 5, 6}
44 * container: {1, 2, 3, 4, 5, 6}
45 * sortPermutation: {1, 0}
45 * sortPermutation: {1, 0}
46 *
46 *
47 * Values will be sorted three by three, and the result will be:
47 * Values will be sorted three by three, and the result will be:
48 * {4, 5, 6, 1, 2, 3}
48 * {4, 5, 6, 1, 2, 3}
49 *
49 *
50 * Example 2:
50 * Example 2:
51 * container: {1, 2, 3, 4, 5, 6}
51 * container: {1, 2, 3, 4, 5, 6}
52 * sortPermutation: {2, 0, 1}
52 * sortPermutation: {2, 0, 1}
53 *
53 *
54 * Values will be sorted two by two, and the result will be:
54 * Values will be sorted two by two, and the result will be:
55 * {5, 6, 1, 2, 3, 4}
55 * {5, 6, 1, 2, 3, 4}
56 *
56 *
57 * @param container the container sorted
57 * @param container the container sorted
58 * @param sortPermutation the indices used to sort the container
58 * @param sortPermutation the indices used to sort the container
59 * @return the container sorted
59 * @return the container sorted
60 * @warning no verification is made on validity of sortPermutation (i.e. the vector has unique
60 * @warning no verification is made on validity of sortPermutation (i.e. the vector has unique
61 * indices and its range is [0 ; vector.size()[ )
61 * indices and its range is [0 ; vector.size()[ )
62 */
62 */
63 template <typename Container>
63 template <typename Container>
64 static Container sort(const Container &container, int nbValues,
64 static Container sort(const Container &container, int nbValues,
65 const std::vector<int> &sortPermutation)
65 const std::vector<int> &sortPermutation)
66 {
66 {
67 auto containerSize = container.size();
67 auto containerSize = container.size();
68 if (containerSize % nbValues != 0
68 if (containerSize % nbValues != 0
69 || ((containerSize / nbValues) != sortPermutation.size())) {
69 || ((containerSize / nbValues) != sortPermutation.size())) {
70 return Container{};
70 return Container{};
71 }
71 }
72
72
73 // Inits result
73 // Inits result
74 auto sortedData = Container{};
74 auto sortedData = Container{};
75 sortedData.reserve(containerSize);
75 sortedData.reserve(containerSize);
76
76
77 for (auto i = 0, componentIndex = 0, permutationIndex = 0; i < containerSize;
77 for (auto i = 0, componentIndex = 0, permutationIndex = 0; i < containerSize;
78 ++i, componentIndex = i % nbValues, permutationIndex = i / nbValues) {
78 ++i, componentIndex = i % nbValues, permutationIndex = i / nbValues) {
79 auto insertIndex = sortPermutation.at(permutationIndex) * nbValues + componentIndex;
79 auto insertIndex = sortPermutation.at(permutationIndex) * nbValues + componentIndex;
80 sortedData.append(container.at(insertIndex));
80 sortedData.push_back(container.at(insertIndex));
81 }
81 }
82
82
83 return sortedData;
83 return sortedData;
84 }
84 }
85
85
86 /**
86 /**
87 * Compares two values that can be NaN. This method is intended to be used as a compare function
87 * Compares two values that can be NaN. This method is intended to be used as a compare function
88 * for searching min value by excluding NaN values.
88 * for searching min value by excluding NaN values.
89 *
89 *
90 * Examples of use:
90 * Examples of use:
91 * - f({1, 3, 2, 4, 5}) will return 1
91 * - f({1, 3, 2, 4, 5}) will return 1
92 * - f({NaN, 3, 2, 4, 5}) will return 2 (NaN is excluded)
92 * - f({NaN, 3, 2, 4, 5}) will return 2 (NaN is excluded)
93 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
93 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
94 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
94 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
95 *
95 *
96 * @param v1 first value
96 * @param v1 first value
97 * @param v2 second value
97 * @param v2 second value
98 * @return true if v1 < v2, false otherwise
98 * @return true if v1 < v2, false otherwise
99 * @sa std::min_element
99 * @sa std::min_element
100 */
100 */
101 template <typename T>
101 template <typename T>
102 static bool minCompareWithNaN(const T &v1, const T &v2)
102 static bool minCompareWithNaN(const T &v1, const T &v2)
103 {
103 {
104 // Table used with NaN values:
104 // Table used with NaN values:
105 // NaN < v2 -> false
105 // NaN < v2 -> false
106 // v1 < NaN -> true
106 // v1 < NaN -> true
107 // NaN < NaN -> false
107 // NaN < NaN -> false
108 // v1 < v2 -> v1 < v2
108 // v1 < v2 -> v1 < v2
109 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
109 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
110 }
110 }
111
111
112 /**
112 /**
113 * Compares two values that can be NaN. This method is intended to be used as a compare function
113 * Compares two values that can be NaN. This method is intended to be used as a compare function
114 * for searching max value by excluding NaN values.
114 * for searching max value by excluding NaN values.
115 *
115 *
116 * Examples of use:
116 * Examples of use:
117 * - f({1, 3, 2, 4, 5}) will return 5
117 * - f({1, 3, 2, 4, 5}) will return 5
118 * - f({1, 3, 2, 4, NaN}) will return 4 (NaN is excluded)
118 * - f({1, 3, 2, 4, NaN}) will return 4 (NaN is excluded)
119 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
119 * - f({NaN, NaN, 3, NaN, NaN}) will return 3 (NaN are excluded)
120 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
120 * - f({NaN, NaN, NaN, NaN, NaN}) will return NaN (no existing value)
121 *
121 *
122 * @param v1 first value
122 * @param v1 first value
123 * @param v2 second value
123 * @param v2 second value
124 * @return true if v1 < v2, false otherwise
124 * @return true if v1 < v2, false otherwise
125 * @sa std::max_element
125 * @sa std::max_element
126 */
126 */
127 template <typename T>
127 template <typename T>
128 static bool maxCompareWithNaN(const T &v1, const T &v2)
128 static bool maxCompareWithNaN(const T &v1, const T &v2)
129 {
129 {
130 // Table used with NaN values:
130 // Table used with NaN values:
131 // NaN < v2 -> true
131 // NaN < v2 -> true
132 // v1 < NaN -> false
132 // v1 < NaN -> false
133 // NaN < NaN -> false
133 // NaN < NaN -> false
134 // v1 < v2 -> v1 < v2
134 // v1 < v2 -> v1 < v2
135 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
135 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
136 }
136 }
137 };
137 };
138
138
139 #endif // SCIQLOP_SORTUTILS_H
139 #endif // SCIQLOP_SORTUTILS_H
@@ -1,379 +1,367
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include "Data/ArrayDataIterator.h"
4 #include "Data/ArrayDataIterator.h"
5 #include <Common/SortUtils.h>
5 #include <Common/SortUtils.h>
6
6
7 #include <QReadLocker>
7 #include <QReadLocker>
8 #include <QReadWriteLock>
8 #include <QReadWriteLock>
9 #include <QVector>
9 #include <QVector>
10
10
11 #include <memory>
11 #include <memory>
12
12
13 template <int Dim>
13 template <int Dim>
14 class ArrayData;
14 class ArrayData;
15
15
16 using DataContainer = QVector<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
97 /// @todo ALX : validate
98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
100 }
100 }
101 catch (const std::bad_cast &) {
101 catch (const std::bad_cast &) {
102 return 0;
102 return 0;
103 }
103 }
104
104
105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
108 }
108 }
109 catch (const std::bad_cast &) {
109 catch (const std::bad_cast &) {
110 return false;
110 return false;
111 }
111 }
112
112
113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
115 return m_It < otherImpl.m_It;
115 return m_It < otherImpl.m_It;
116 }
116 }
117 catch (const std::bad_cast &) {
117 catch (const std::bad_cast &) {
118 return false;
118 return false;
119 }
119 }
120
120
121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
122 {
122 {
123 auto result = clone();
123 auto result = clone();
124 while (offset--) {
124 result->next(offset);
125 result->next();
126 }
127 return result;
125 return result;
128 }
126 }
129
127
130 void next() override { std::advance(m_It, m_NbComponents); }
128 void next(int offset) override { std::advance(m_It, offset * m_NbComponents); }
131 void prev() override { std::advance(m_It, -m_NbComponents); }
129 void prev() override { std::advance(m_It, -m_NbComponents); }
132
130
133 double at(int componentIndex) const override { return *(m_It + componentIndex); }
131 double at(int componentIndex) const override { return *(m_It + componentIndex); }
134 double first() const override { return *m_It; }
132 double first() const override { return *m_It; }
135 double min() const override
133 double min() const override
136 {
134 {
137 auto values = this->values();
135 auto values = this->values();
138 auto end = values.cend();
136 auto end = values.cend();
139 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
137 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
140 return SortUtils::minCompareWithNaN(v1, v2);
138 return SortUtils::minCompareWithNaN(v1, v2);
141 });
139 });
142
140
143 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
141 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
144 }
142 }
145 double max() const override
143 double max() const override
146 {
144 {
147 auto values = this->values();
145 auto values = this->values();
148 auto end = values.cend();
146 auto end = values.cend();
149 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
147 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
150 return SortUtils::maxCompareWithNaN(v1, v2);
148 return SortUtils::maxCompareWithNaN(v1, v2);
151 });
149 });
152 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
150 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
153 }
151 }
154
152
155 QVector<double> values() const override
153 QVector<double> values() const override
156 {
154 {
157 auto result = QVector<double>{};
155 auto result = QVector<double>{};
158 for (auto i = 0; i < m_NbComponents; ++i) {
156 for (auto i = 0; i < m_NbComponents; ++i) {
159 result.push_back(*(m_It + i));
157 result.push_back(*(m_It + i));
160 }
158 }
161
159
162 return result;
160 return result;
163 }
161 }
164
162
165 void swap(ArrayDataIteratorValue::Impl &other) override
163 void swap(ArrayDataIteratorValue::Impl &other) override
166 {
164 {
167 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
165 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
168 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
166 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
169 }
167 }
170
168
171 private:
169 private:
172 DataContainerIterator m_It;
170 DataContainerIterator m_It;
173 int m_NbComponents;
171 int m_NbComponents;
174 };
172 };
175
173
176 } // namespace arraydata_detail
174 } // namespace arraydata_detail
177
175
178 /**
176 /**
179 * @brief The ArrayData class represents a dataset for a data series.
177 * @brief The ArrayData class represents a dataset for a data series.
180 *
178 *
181 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
179 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
182 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
180 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
183 * number of values
181 * number of values
184 *
182 *
185 * @tparam Dim the dimension of the ArrayData (one or two)
183 * @tparam Dim the dimension of the ArrayData (one or two)
186 * @sa IDataSeries
184 * @sa IDataSeries
187 */
185 */
188 template <int Dim>
186 template <int Dim>
189 class ArrayData {
187 class ArrayData {
190 public:
188 public:
191 // ///// //
189 // ///// //
192 // Ctors //
190 // Ctors //
193 // ///// //
191 // ///// //
194
192
195 /**
193 /**
196 * Ctor for a unidimensional ArrayData
194 * Ctor for a unidimensional ArrayData
197 * @param data the data the ArrayData will hold
195 * @param data the data the ArrayData will hold
198 */
196 */
199 template <int D = Dim, typename = std::enable_if_t<D == 1> >
197 template <int D = Dim, typename = std::enable_if_t<D == 1> >
200 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
198 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
201 {
199 {
202 }
200 }
203
201
204 /**
202 /**
205 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
203 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
206 * greater than 2 and must be a divisor of the total number of data in the vector
204 * greater than 2 and must be a divisor of the total number of data in the vector
207 * @param data the data the ArrayData will hold
205 * @param data the data the ArrayData will hold
208 * @param nbComponents the number of components
206 * @param nbComponents the number of components
209 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
207 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
210 * of the size of the data
208 * of the size of the data
211 */
209 */
212 template <int D = Dim, typename = std::enable_if_t<D == 2> >
210 template <int D = Dim, typename = std::enable_if_t<D == 2> >
213 explicit ArrayData(DataContainer data, int nbComponents)
211 explicit ArrayData(DataContainer data, int nbComponents)
214 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
212 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
215 {
213 {
216 if (nbComponents < 2) {
214 if (nbComponents < 2) {
217 throw std::invalid_argument{
215 throw std::invalid_argument{
218 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
216 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
219 .arg(nbComponents)
217 .arg(nbComponents)
220 .toStdString()};
218 .toStdString()};
221 }
219 }
222
220
223 if (m_Data.size() % m_NbComponents != 0) {
221 if (m_Data.size() % m_NbComponents != 0) {
224 throw std::invalid_argument{QString{
222 throw std::invalid_argument{QString{
225 "The number of components (%1) is inconsistent with the total number of data (%2)"}
223 "The number of components (%1) is inconsistent with the total number of data (%2)"}
226 .arg(m_Data.size(), nbComponents)
224 .arg(m_Data.size(), nbComponents)
227 .toStdString()};
225 .toStdString()};
228 }
226 }
229 }
227 }
230
228
231 /// Copy ctor
229 /// Copy ctor
232 explicit ArrayData(const ArrayData &other)
230 explicit ArrayData(const ArrayData &other)
233 {
231 {
234 QReadLocker otherLocker{&other.m_Lock};
232 QReadLocker otherLocker{&other.m_Lock};
235 m_Data = other.m_Data;
233 m_Data = other.m_Data;
236 m_NbComponents = other.m_NbComponents;
234 m_NbComponents = other.m_NbComponents;
237 }
235 }
238
236
239 // /////////////// //
237 // /////////////// //
240 // General methods //
238 // General methods //
241 // /////////////// //
239 // /////////////// //
242
240
243 /**
241 /**
244 * Merges into the array data an other array data. The two array datas must have the same number
242 * Merges into the array data an other array data. The two array datas must have the same number
245 * of components so the merge can be done
243 * of components so the merge can be done
246 * @param other the array data to merge with
244 * @param other the array data to merge with
247 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
245 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
248 * inserted at the end
246 * inserted at the end
249 */
247 */
250 void add(const ArrayData<Dim> &other, bool prepend = false)
248 void add(const ArrayData<Dim> &other, bool prepend = false)
251 {
249 {
252 QWriteLocker locker{&m_Lock};
250 QWriteLocker locker{&m_Lock};
253 QReadLocker otherLocker{&other.m_Lock};
251 QReadLocker otherLocker{&other.m_Lock};
254
252
255 if (m_NbComponents != other.componentCount()) {
253 if (m_NbComponents != other.componentCount()) {
256 return;
254 return;
257 }
255 }
258
256
259 if (prepend) {
257 insert(other.cbegin(), other.cend(), prepend);
260 auto otherDataSize = other.m_Data.size();
261 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
262 for (auto i = 0; i < otherDataSize; ++i) {
263 m_Data.replace(i, other.m_Data.at(i));
264 }
265 }
266 else {
267 m_Data.append(other.m_Data);
268 }
269 }
258 }
270
259
271 void clear()
260 void clear()
272 {
261 {
273 QWriteLocker locker{&m_Lock};
262 QWriteLocker locker{&m_Lock};
274 m_Data.clear();
263 m_Data.clear();
275 }
264 }
276
265
277 int componentCount() const noexcept { return m_NbComponents; }
266 int componentCount() const noexcept { return m_NbComponents; }
278
267
279 /// @return the size (i.e. number of values) of a single component
268 /// @return the size (i.e. number of values) of a single component
280 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
269 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
281 int size() const
270 int size() const
282 {
271 {
283 QReadLocker locker{&m_Lock};
272 QReadLocker locker{&m_Lock};
284 return m_Data.size() / m_NbComponents;
273 return m_Data.size() / m_NbComponents;
285 }
274 }
286
275
287 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
276 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
288 {
277 {
289 QReadLocker locker{&m_Lock};
278 QReadLocker locker{&m_Lock};
290 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
279 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
291 }
280 }
292
281
293 // ///////// //
282 // ///////// //
294 // Iterators //
283 // Iterators //
295 // ///////// //
284 // ///////// //
296
285
297 ArrayDataIterator begin()
286 ArrayDataIterator begin()
298 {
287 {
299 return ArrayDataIterator{
288 return ArrayDataIterator{
300 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
289 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
301 m_Data, m_NbComponents, true)}};
290 m_Data, m_NbComponents, true)}};
302 }
291 }
303
292
304 ArrayDataIterator end()
293 ArrayDataIterator end()
305 {
294 {
306 return ArrayDataIterator{
295 return ArrayDataIterator{
307 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
296 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
308 m_Data, m_NbComponents, false)}};
297 m_Data, m_NbComponents, false)}};
309 }
298 }
310
299
311 ArrayDataIterator cbegin() const
300 ArrayDataIterator cbegin() const
312 {
301 {
313 return ArrayDataIterator{
302 return ArrayDataIterator{
314 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
303 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
315 m_Data, m_NbComponents, true)}};
304 m_Data, m_NbComponents, true)}};
316 }
305 }
317
306
318 ArrayDataIterator cend() const
307 ArrayDataIterator cend() const
319 {
308 {
320 return ArrayDataIterator{
309 return ArrayDataIterator{
321 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
310 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
322 m_Data, m_NbComponents, false)}};
311 m_Data, m_NbComponents, false)}};
323 }
312 }
324
313
325 void erase(ArrayDataIterator first, ArrayDataIterator last)
314 void erase(ArrayDataIterator first, ArrayDataIterator last)
326 {
315 {
327 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
316 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
328 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
317 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
329
318
330 if (firstImpl && lastImpl) {
319 if (firstImpl && lastImpl) {
331 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
320 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
332 }
321 }
333 }
322 }
334
323
335 /// Inserts at the end of the array data the values passed as a parameter. This
324 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
336 /// method is intended to be used in the context of generating a back insert iterator, or only
337 /// if it's ensured that the total size of the vector is consistent with the number of
338 /// components of the array data
339 /// @param values the values to insert
340 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
341 void push_back(const QVector<double> &values)
342 {
325 {
343 Q_ASSERT(values.size() % m_NbComponents == 0);
326 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
344 m_Data.append(values);
327 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
328
329 if (firstImpl && lastImpl) {
330 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
331
332 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
333 }
345 }
334 }
346
335
347 /**
336 /**
348 * @return the data at a specified index
337 * @return the data at a specified index
349 * @remarks index must be a valid position
338 * @remarks index must be a valid position
350 */
339 */
351 double at(int index) const noexcept
340 double at(int index) const noexcept
352 {
341 {
353 QReadLocker locker{&m_Lock};
342 QReadLocker locker{&m_Lock};
354 return m_Data.at(index);
343 return m_Data.at(index);
355 }
344 }
356
345
357 // ///////////// //
346 // ///////////// //
358 // 1-dim methods //
347 // 1-dim methods //
359 // ///////////// //
348 // ///////////// //
360
349
361 /**
350 /**
362 * @return the data as a vector, as a const reference
351 * @return the data as a vector, as a const reference
363 * @remarks this method is only available for a unidimensional ArrayData
352 * @remarks this method is only available for a unidimensional ArrayData
364 */
353 */
365 template <int D = Dim, typename = std::enable_if_t<D == 1> >
354 template <int D = Dim, typename = std::enable_if_t<D == 1> >
366 const QVector<double> &cdata() const noexcept
355 DataContainer cdata() const noexcept
367 {
356 {
368 QReadLocker locker{&m_Lock};
369 return m_Data;
357 return m_Data;
370 }
358 }
371
359
372 private:
360 private:
373 DataContainer m_Data;
361 DataContainer m_Data;
374 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
362 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
375 int m_NbComponents;
363 int m_NbComponents;
376 mutable QReadWriteLock m_Lock;
364 mutable QReadWriteLock m_Lock;
377 };
365 };
378
366
379 #endif // SCIQLOP_ARRAYDATA_H
367 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,74 +1,74
1 #ifndef SCIQLOP_ARRAYDATAITERATOR_H
1 #ifndef SCIQLOP_ARRAYDATAITERATOR_H
2 #define SCIQLOP_ARRAYDATAITERATOR_H
2 #define SCIQLOP_ARRAYDATAITERATOR_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
5 #include "Data/SqpIterator.h"
6
6
7 #include <QVector>
7 #include <QVector>
8 #include <memory>
8 #include <memory>
9
9
10 /**
10 /**
11 * @brief The ArrayDataIteratorValue class represents the current value of an array data iterator.
11 * @brief The ArrayDataIteratorValue class represents the current value of an array data iterator.
12 * It offers standard access methods for the data in the series (at(), first()), but it is up to
12 * It offers standard access methods for the data in the series (at(), first()), but it is up to
13 * each array data to define its own implementation of how to retrieve this data (one-dim or two-dim
13 * each array data to define its own implementation of how to retrieve this data (one-dim or two-dim
14 * array), by implementing the ArrayDataIteratorValue::Impl interface
14 * array), by implementing the ArrayDataIteratorValue::Impl interface
15 * @sa ArrayDataIterator
15 * @sa ArrayDataIterator
16 */
16 */
17 class SCIQLOP_CORE_EXPORT ArrayDataIteratorValue {
17 class SCIQLOP_CORE_EXPORT ArrayDataIteratorValue {
18 public:
18 public:
19 struct Impl {
19 struct Impl {
20 virtual ~Impl() noexcept = default;
20 virtual ~Impl() noexcept = default;
21 virtual std::unique_ptr<Impl> clone() const = 0;
21 virtual std::unique_ptr<Impl> clone() const = 0;
22 virtual int distance(const Impl &other) const = 0;
22 virtual int distance(const Impl &other) const = 0;
23 virtual bool equals(const Impl &other) const = 0;
23 virtual bool equals(const Impl &other) const = 0;
24 virtual bool lowerThan(const Impl &other) const = 0;
24 virtual bool lowerThan(const Impl &other) const = 0;
25 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
25 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
26 virtual void next() = 0;
26 virtual void next(int offset) = 0;
27 virtual void prev() = 0;
27 virtual void prev() = 0;
28 virtual double at(int componentIndex) const = 0;
28 virtual double at(int componentIndex) const = 0;
29 virtual double first() const = 0;
29 virtual double first() const = 0;
30 virtual double min() const = 0;
30 virtual double min() const = 0;
31 virtual double max() const = 0;
31 virtual double max() const = 0;
32 virtual QVector<double> values() const = 0;
32 virtual QVector<double> values() const = 0;
33
33
34 virtual void swap(Impl &other) = 0;
34 virtual void swap(Impl &other) = 0;
35 };
35 };
36
36
37 explicit ArrayDataIteratorValue(std::unique_ptr<Impl> impl);
37 explicit ArrayDataIteratorValue(std::unique_ptr<Impl> impl);
38 ArrayDataIteratorValue(const ArrayDataIteratorValue &other);
38 ArrayDataIteratorValue(const ArrayDataIteratorValue &other);
39 ArrayDataIteratorValue &operator=(ArrayDataIteratorValue other);
39 ArrayDataIteratorValue &operator=(ArrayDataIteratorValue other);
40
40
41 int distance(const ArrayDataIteratorValue &other) const;
41 int distance(const ArrayDataIteratorValue &other) const;
42 bool equals(const ArrayDataIteratorValue &other) const;
42 bool equals(const ArrayDataIteratorValue &other) const;
43 bool lowerThan(const ArrayDataIteratorValue &other) const;
43 bool lowerThan(const ArrayDataIteratorValue &other) const;
44
44
45 ArrayDataIteratorValue advance(int offset) const;
45 ArrayDataIteratorValue advance(int offset) const;
46 /// Advances to the next value
46 /// Advances to the next value
47 void next();
47 void next(int offset = 1);
48 /// Moves back to the previous value
48 /// Moves back to the previous value
49 void prev();
49 void prev();
50 /// Gets value of a specified component
50 /// Gets value of a specified component
51 double at(int componentIndex) const;
51 double at(int componentIndex) const;
52 /// Gets value of first component
52 /// Gets value of first component
53 double first() const;
53 double first() const;
54 /// Gets min value among all components
54 /// Gets min value among all components
55 double min() const;
55 double min() const;
56 /// Gets max value among all components
56 /// Gets max value among all components
57 double max() const;
57 double max() const;
58 /// Gets all values
58 /// Gets all values
59 QVector<double> values() const;
59 QVector<double> values() const;
60
60
61 Impl *impl();
61 Impl *impl();
62
62
63 friend void swap(ArrayDataIteratorValue &lhs, ArrayDataIteratorValue &rhs)
63 friend void swap(ArrayDataIteratorValue &lhs, ArrayDataIteratorValue &rhs)
64 {
64 {
65 std::swap(lhs.m_Impl, rhs.m_Impl);
65 std::swap(lhs.m_Impl, rhs.m_Impl);
66 }
66 }
67
67
68 private:
68 private:
69 std::unique_ptr<Impl> m_Impl;
69 std::unique_ptr<Impl> m_Impl;
70 };
70 };
71
71
72 using ArrayDataIterator = SqpIterator<ArrayDataIteratorValue>;
72 using ArrayDataIterator = SqpIterator<ArrayDataIteratorValue>;
73
73
74 #endif // SCIQLOP_ARRAYDATAITERATOR_H
74 #endif // SCIQLOP_ARRAYDATAITERATOR_H
@@ -1,410 +1,399
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 while (offset--) {
84 result->next(offset);
85 result->next();
86 }
87 return result;
85 return result;
88 }
86 }
89
87
90 void next() override
88 void next(int offset) override
91 {
89 {
92 ++m_XIt;
90 m_XIt->next(offset);
93 ++m_ValuesIt;
91 m_ValuesIt->next(offset);
94 }
92 }
95
93
96 void prev() override
94 void prev() override
97 {
95 {
98 --m_XIt;
96 --m_XIt;
99 --m_ValuesIt;
97 --m_ValuesIt;
100 }
98 }
101
99
102 double x() const override { return m_XIt->at(0); }
100 double x() const override { return m_XIt->at(0); }
103 double value() const override { return m_ValuesIt->at(0); }
101 double value() const override { return m_ValuesIt->at(0); }
104 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
102 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
105 double minValue() const override { return m_ValuesIt->min(); }
103 double minValue() const override { return m_ValuesIt->min(); }
106 double maxValue() const override { return m_ValuesIt->max(); }
104 double maxValue() const override { return m_ValuesIt->max(); }
107 QVector<double> values() const override { return m_ValuesIt->values(); }
105 QVector<double> values() const override { return m_ValuesIt->values(); }
108
106
109 void swap(DataSeriesIteratorValue::Impl &other) override
107 void swap(DataSeriesIteratorValue::Impl &other) override
110 {
108 {
111 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
109 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
112 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
110 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
113 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
111 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
114 }
112 }
115
113
116 private:
114 private:
117 ArrayDataIterator m_XIt;
115 ArrayDataIterator m_XIt;
118 ArrayDataIterator m_ValuesIt;
116 ArrayDataIterator m_ValuesIt;
119 };
117 };
120 } // namespace dataseries_detail
118 } // namespace dataseries_detail
121
119
122 /**
120 /**
123 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
121 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
124 *
122 *
125 * It proposes to set a dimension for the values ​​data.
123 * It proposes to set a dimension for the values ​​data.
126 *
124 *
127 * A DataSeries is always sorted on its x-axis data.
125 * A DataSeries is always sorted on its x-axis data.
128 *
126 *
129 * @tparam Dim The dimension of the values data
127 * @tparam Dim The dimension of the values data
130 *
128 *
131 */
129 */
132 template <int Dim>
130 template <int Dim>
133 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
131 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
134 friend class DataSeriesMergeHelper;
132 friend class DataSeriesMergeHelper;
135
133
136 public:
134 public:
137 /// Tag needed to define the push_back() method
138 /// @sa push_back()
139 using value_type = DataSeriesIteratorValue;
140
141 /// @sa IDataSeries::xAxisData()
135 /// @sa IDataSeries::xAxisData()
142 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
136 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
143 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
137 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
144
138
145 /// @sa IDataSeries::xAxisUnit()
139 /// @sa IDataSeries::xAxisUnit()
146 Unit xAxisUnit() const override { return m_XAxisUnit; }
140 Unit xAxisUnit() const override { return m_XAxisUnit; }
147
141
148 /// @return the values dataset
142 /// @return the values dataset
149 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
143 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
150 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
144 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
151
145
152 /// @sa IDataSeries::valuesUnit()
146 /// @sa IDataSeries::valuesUnit()
153 Unit valuesUnit() const override { return m_ValuesUnit; }
147 Unit valuesUnit() const override { return m_ValuesUnit; }
154
148
155
149
156 SqpRange range() const override
150 SqpRange range() const override
157 {
151 {
158 if (!m_XAxisData->cdata().isEmpty()) {
152 if (!m_XAxisData->cdata().empty()) {
159 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
153 return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()};
160 }
154 }
161
155
162 return SqpRange{};
156 return SqpRange{};
163 }
157 }
164
158
165 void clear()
159 void clear()
166 {
160 {
167 m_XAxisData->clear();
161 m_XAxisData->clear();
168 m_ValuesData->clear();
162 m_ValuesData->clear();
169 }
163 }
170
164
171 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
165 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
172
166
173 /// Merges into the data series an other data series
167 /// Merges into the data series an other data series
174 /// @remarks the data series to merge with is cleared after the operation
168 /// @remarks the data series to merge with is cleared after the operation
175 void merge(IDataSeries *dataSeries) override
169 void merge(IDataSeries *dataSeries) override
176 {
170 {
177 dataSeries->lockWrite();
171 dataSeries->lockWrite();
178 lockWrite();
172 lockWrite();
179
173
180 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
174 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
181 DataSeriesMergeHelper::merge(*other, *this);
175 DataSeriesMergeHelper::merge(*other, *this);
182 }
176 }
183 else {
177 else {
184 qCWarning(LOG_DataSeries())
178 qCWarning(LOG_DataSeries())
185 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
179 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
186 }
180 }
187 unlock();
181 unlock();
188 dataSeries->unlock();
182 dataSeries->unlock();
189 }
183 }
190
184
191 void purge(double min, double max) override
185 void purge(double min, double max) override
192 {
186 {
193 // Nothing to purge if series is empty
187 // Nothing to purge if series is empty
194 if (isEmpty()) {
188 if (isEmpty()) {
195 return;
189 return;
196 }
190 }
197
191
198 if (min > max) {
192 if (min > max) {
199 std::swap(min, max);
193 std::swap(min, max);
200 }
194 }
201
195
202 // Nothing to purge if series min/max are inside purge range
196 // Nothing to purge if series min/max are inside purge range
203 auto xMin = cbegin()->x();
197 auto xMin = cbegin()->x();
204 auto xMax = (--cend())->x();
198 auto xMax = (--cend())->x();
205 if (xMin >= min && xMax <= max) {
199 if (xMin >= min && xMax <= max) {
206 return;
200 return;
207 }
201 }
208
202
209 auto lowerIt = std::lower_bound(
203 auto lowerIt = std::lower_bound(
210 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
204 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
211 erase(begin(), lowerIt);
205 erase(begin(), lowerIt);
212 auto upperIt = std::upper_bound(
206 auto upperIt = std::upper_bound(
213 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
207 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
214 erase(upperIt, end());
208 erase(upperIt, end());
215 }
209 }
216
210
217 // ///////// //
211 // ///////// //
218 // Iterators //
212 // Iterators //
219 // ///////// //
213 // ///////// //
220
214
221 DataSeriesIterator begin() override
215 DataSeriesIterator begin() override
222 {
216 {
223 return DataSeriesIterator{DataSeriesIteratorValue{
217 return DataSeriesIterator{DataSeriesIteratorValue{
224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
218 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
225 }
219 }
226
220
227 DataSeriesIterator end() override
221 DataSeriesIterator end() override
228 {
222 {
229 return DataSeriesIterator{DataSeriesIteratorValue{
223 return DataSeriesIterator{DataSeriesIteratorValue{
230 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
231 }
225 }
232
226
233 DataSeriesIterator cbegin() const override
227 DataSeriesIterator cbegin() const override
234 {
228 {
235 return DataSeriesIterator{DataSeriesIteratorValue{
229 return DataSeriesIterator{DataSeriesIteratorValue{
236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
230 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
237 }
231 }
238
232
239 DataSeriesIterator cend() const override
233 DataSeriesIterator cend() const override
240 {
234 {
241 return DataSeriesIterator{DataSeriesIteratorValue{
235 return DataSeriesIterator{DataSeriesIteratorValue{
242 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
243 }
237 }
244
238
245 void erase(DataSeriesIterator first, DataSeriesIterator last)
239 void erase(DataSeriesIterator first, DataSeriesIterator last)
246 {
240 {
247 auto firstImpl
241 auto firstImpl
248 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
242 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
249 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
243 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
250
244
251 if (firstImpl && lastImpl) {
245 if (firstImpl && lastImpl) {
252 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
246 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
253 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
247 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
254 }
248 }
255 }
249 }
256
250
251 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
252 {
253 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
254 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
255
256 if (firstImpl && lastImpl) {
257 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
258 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
259 }
260 }
261
257 /// @sa IDataSeries::minXAxisData()
262 /// @sa IDataSeries::minXAxisData()
258 DataSeriesIterator minXAxisData(double minXAxisData) const override
263 DataSeriesIterator minXAxisData(double minXAxisData) const override
259 {
264 {
260 return std::lower_bound(
265 return std::lower_bound(
261 cbegin(), cend(), minXAxisData,
266 cbegin(), cend(), minXAxisData,
262 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
267 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
263 }
268 }
264
269
265 /// @sa IDataSeries::maxXAxisData()
270 /// @sa IDataSeries::maxXAxisData()
266 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
271 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
267 {
272 {
268 // Gets the first element that greater than max value
273 // Gets the first element that greater than max value
269 auto it = std::upper_bound(
274 auto it = std::upper_bound(
270 cbegin(), cend(), maxXAxisData,
275 cbegin(), cend(), maxXAxisData,
271 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
276 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
272
277
273 return it == cbegin() ? cend() : --it;
278 return it == cbegin() ? cend() : --it;
274 }
279 }
275
280
276 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
281 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
277 double maxXAxisData) const override
282 double maxXAxisData) const override
278 {
283 {
279 if (minXAxisData > maxXAxisData) {
284 if (minXAxisData > maxXAxisData) {
280 std::swap(minXAxisData, maxXAxisData);
285 std::swap(minXAxisData, maxXAxisData);
281 }
286 }
282
287
283 auto begin = cbegin();
288 auto begin = cbegin();
284 auto end = cend();
289 auto end = cend();
285
290
286 auto lowerIt = std::lower_bound(
291 auto lowerIt = std::lower_bound(
287 begin, end, minXAxisData,
292 begin, end, minXAxisData,
288 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
293 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
289 auto upperIt = std::upper_bound(
294 auto upperIt = std::upper_bound(
290 begin, end, maxXAxisData,
295 lowerIt, end, maxXAxisData,
291 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
296 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
292
297
293 return std::make_pair(lowerIt, upperIt);
298 return std::make_pair(lowerIt, upperIt);
294 }
299 }
295
300
296 std::pair<DataSeriesIterator, DataSeriesIterator>
301 std::pair<DataSeriesIterator, DataSeriesIterator>
297 valuesBounds(double minXAxisData, double maxXAxisData) const override
302 valuesBounds(double minXAxisData, double maxXAxisData) const override
298 {
303 {
299 // Places iterators to the correct x-axis range
304 // Places iterators to the correct x-axis range
300 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
305 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
301
306
302 // Returns end iterators if the range is empty
307 // Returns end iterators if the range is empty
303 if (xAxisRangeIts.first == xAxisRangeIts.second) {
308 if (xAxisRangeIts.first == xAxisRangeIts.second) {
304 return std::make_pair(cend(), cend());
309 return std::make_pair(cend(), cend());
305 }
310 }
306
311
307 // Gets the iterator on the min of all values data
312 // Gets the iterator on the min of all values data
308 auto minIt = std::min_element(
313 auto minIt = std::min_element(
309 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
314 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
310 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
315 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
311 });
316 });
312
317
313 // Gets the iterator on the max of all values data
318 // Gets the iterator on the max of all values data
314 auto maxIt = std::max_element(
319 auto maxIt = std::max_element(
315 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
320 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
316 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
321 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
317 });
322 });
318
323
319 return std::make_pair(minIt, maxIt);
324 return std::make_pair(minIt, maxIt);
320 }
325 }
321
326
322 // /////// //
327 // /////// //
323 // Mutexes //
328 // Mutexes //
324 // /////// //
329 // /////// //
325
330
326 virtual void lockRead() { m_Lock.lockForRead(); }
331 virtual void lockRead() { m_Lock.lockForRead(); }
327 virtual void lockWrite() { m_Lock.lockForWrite(); }
332 virtual void lockWrite() { m_Lock.lockForWrite(); }
328 virtual void unlock() { m_Lock.unlock(); }
333 virtual void unlock() { m_Lock.unlock(); }
329
334
330 // ///// //
331 // Other //
332 // ///// //
333
334 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
335 /// method is intended to be used in the context of generating a back insert iterator
336 /// @param iteratorValue the iterator value containing the values to insert
337 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
338 /// @sa merge()
339 /// @sa value_type
340 void push_back(const value_type &iteratorValue)
341 {
342 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
343 m_ValuesData->push_back(iteratorValue.values());
344 }
345
346 protected:
335 protected:
347 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
336 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
348 /// DataSeries with no values will be created.
337 /// DataSeries with no values will be created.
349 /// @remarks data series is automatically sorted on its x-axis data
338 /// @remarks data series is automatically sorted on its x-axis data
350 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
339 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
351 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
340 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
352 : m_XAxisData{xAxisData},
341 : m_XAxisData{xAxisData},
353 m_XAxisUnit{xAxisUnit},
342 m_XAxisUnit{xAxisUnit},
354 m_ValuesData{valuesData},
343 m_ValuesData{valuesData},
355 m_ValuesUnit{valuesUnit}
344 m_ValuesUnit{valuesUnit}
356 {
345 {
357 if (m_XAxisData->size() != m_ValuesData->size()) {
346 if (m_XAxisData->size() != m_ValuesData->size()) {
358 clear();
347 clear();
359 }
348 }
360
349
361 // Sorts data if it's not the case
350 // Sorts data if it's not the case
362 const auto &xAxisCData = m_XAxisData->cdata();
351 const auto &xAxisCData = m_XAxisData->cdata();
363 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
352 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
364 sort();
353 sort();
365 }
354 }
366 }
355 }
367
356
368 /// Copy ctor
357 /// Copy ctor
369 explicit DataSeries(const DataSeries<Dim> &other)
358 explicit DataSeries(const DataSeries<Dim> &other)
370 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
359 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
371 m_XAxisUnit{other.m_XAxisUnit},
360 m_XAxisUnit{other.m_XAxisUnit},
372 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
361 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
373 m_ValuesUnit{other.m_ValuesUnit}
362 m_ValuesUnit{other.m_ValuesUnit}
374 {
363 {
375 // Since a series is ordered from its construction and is always ordered, it is not
364 // Since a series is ordered from its construction and is always ordered, it is not
376 // necessary to call the sort method here ('other' is sorted)
365 // necessary to call the sort method here ('other' is sorted)
377 }
366 }
378
367
379 /// Assignment operator
368 /// Assignment operator
380 template <int D>
369 template <int D>
381 DataSeries &operator=(DataSeries<D> other)
370 DataSeries &operator=(DataSeries<D> other)
382 {
371 {
383 std::swap(m_XAxisData, other.m_XAxisData);
372 std::swap(m_XAxisData, other.m_XAxisData);
384 std::swap(m_XAxisUnit, other.m_XAxisUnit);
373 std::swap(m_XAxisUnit, other.m_XAxisUnit);
385 std::swap(m_ValuesData, other.m_ValuesData);
374 std::swap(m_ValuesData, other.m_ValuesData);
386 std::swap(m_ValuesUnit, other.m_ValuesUnit);
375 std::swap(m_ValuesUnit, other.m_ValuesUnit);
387
376
388 return *this;
377 return *this;
389 }
378 }
390
379
391 private:
380 private:
392 /**
381 /**
393 * Sorts data series on its x-axis data
382 * Sorts data series on its x-axis data
394 */
383 */
395 void sort() noexcept
384 void sort() noexcept
396 {
385 {
397 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
386 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
398 m_XAxisData = m_XAxisData->sort(permutation);
387 m_XAxisData = m_XAxisData->sort(permutation);
399 m_ValuesData = m_ValuesData->sort(permutation);
388 m_ValuesData = m_ValuesData->sort(permutation);
400 }
389 }
401
390
402 std::shared_ptr<ArrayData<1> > m_XAxisData;
391 std::shared_ptr<ArrayData<1> > m_XAxisData;
403 Unit m_XAxisUnit;
392 Unit m_XAxisUnit;
404 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
393 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
405 Unit m_ValuesUnit;
394 Unit m_ValuesUnit;
406
395
407 QReadWriteLock m_Lock;
396 QReadWriteLock m_Lock;
408 };
397 };
409
398
410 #endif // SCIQLOP_DATASERIES_H
399 #endif // SCIQLOP_DATASERIES_H
@@ -1,78 +1,78
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
1 #ifndef SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
2 #define SCIQLOP_DATASERIESITERATOR_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
5 #include "Data/SqpIterator.h"
6
6
7 #include <QVector>
7 #include <QVector>
8 #include <memory>
8 #include <memory>
9
9
10 /**
10 /**
11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
11 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
12 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
13 * each series to define its own implementation of how to retrieve this data, by implementing the
13 * each series to define its own implementation of how to retrieve this data, by implementing the
14 * DataSeriesIteratorValue::Impl interface
14 * DataSeriesIteratorValue::Impl interface
15 *
15 *
16 * @sa DataSeriesIterator
16 * @sa DataSeriesIterator
17 */
17 */
18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
18 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
19 public:
19 public:
20 struct Impl {
20 struct Impl {
21 virtual ~Impl() noexcept = default;
21 virtual ~Impl() noexcept = default;
22 virtual std::unique_ptr<Impl> clone() const = 0;
22 virtual std::unique_ptr<Impl> clone() const = 0;
23 virtual int distance(const Impl &other) const = 0;
23 virtual int distance(const Impl &other) const = 0;
24 virtual bool equals(const Impl &other) const = 0;
24 virtual bool equals(const Impl &other) const = 0;
25 virtual bool lowerThan(const Impl &other) const = 0;
25 virtual bool lowerThan(const Impl &other) const = 0;
26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
26 virtual std::unique_ptr<Impl> advance(int offset) const = 0;
27 virtual void next() = 0;
27 virtual void next(int offset) = 0;
28 virtual void prev() = 0;
28 virtual void prev() = 0;
29 virtual double x() const = 0;
29 virtual double x() const = 0;
30 virtual double value() const = 0;
30 virtual double value() const = 0;
31 virtual double value(int componentIndex) const = 0;
31 virtual double value(int componentIndex) const = 0;
32 virtual double minValue() const = 0;
32 virtual double minValue() const = 0;
33 virtual double maxValue() const = 0;
33 virtual double maxValue() const = 0;
34 virtual QVector<double> values() const = 0;
34 virtual QVector<double> values() const = 0;
35
35
36 virtual void swap(Impl &other) = 0;
36 virtual void swap(Impl &other) = 0;
37 };
37 };
38
38
39 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
39 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
40 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
40 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
41 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
41 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
42
42
43 int distance(const DataSeriesIteratorValue &other) const;
43 int distance(const DataSeriesIteratorValue &other) const;
44 bool equals(const DataSeriesIteratorValue &other) const;
44 bool equals(const DataSeriesIteratorValue &other) const;
45 bool lowerThan(const DataSeriesIteratorValue &other) const;
45 bool lowerThan(const DataSeriesIteratorValue &other) const;
46
46
47 DataSeriesIteratorValue advance(int offset) const;
47 DataSeriesIteratorValue advance(int offset) const;
48 /// Advances to the next value
48 /// Advances to the next value
49 void next();
49 void next(int offset = 1);
50 /// Moves back to the previous value
50 /// Moves back to the previous value
51 void prev();
51 void prev();
52 /// Gets x-axis data
52 /// Gets x-axis data
53 double x() const;
53 double x() const;
54 /// Gets value data
54 /// Gets value data
55 double value() const;
55 double value() const;
56 /// Gets value data depending on an index
56 /// Gets value data depending on an index
57 double value(int componentIndex) const;
57 double value(int componentIndex) const;
58 /// Gets min of all values data
58 /// Gets min of all values data
59 double minValue() const;
59 double minValue() const;
60 /// Gets max of all values data
60 /// Gets max of all values data
61 double maxValue() const;
61 double maxValue() const;
62 /// Gets all values data
62 /// Gets all values data
63 QVector<double> values() const;
63 QVector<double> values() const;
64
64
65 Impl *impl();
65 Impl *impl();
66
66
67 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
67 friend void swap(DataSeriesIteratorValue &lhs, DataSeriesIteratorValue &rhs)
68 {
68 {
69 std::swap(lhs.m_Impl, rhs.m_Impl);
69 std::swap(lhs.m_Impl, rhs.m_Impl);
70 }
70 }
71
71
72 private:
72 private:
73 std::unique_ptr<Impl> m_Impl;
73 std::unique_ptr<Impl> m_Impl;
74 };
74 };
75
75
76 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
76 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
77
77
78 #endif // SCIQLOP_DATASERIESITERATOR_H
78 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -1,132 +1,83
1 #ifndef SCIQLOP_DATASERIESMERGEHELPER_H
1 #ifndef SCIQLOP_DATASERIESMERGEHELPER_H
2 #define SCIQLOP_DATASERIESMERGEHELPER_H
2 #define SCIQLOP_DATASERIESMERGEHELPER_H
3
3
4 template <int Dim>
4 template <int Dim>
5 class DataSeries;
5 class DataSeries;
6
6
7 namespace detail {
7 namespace detail {
8
8
9 /**
9 /**
10 * Scope that can be used for a merge operation
10 * Scope that can be used for a merge operation
11 * @tparam FEnd the type of function that will be executed at the end of the scope
11 * @tparam FEnd the type of function that will be executed at the end of the scope
12 */
12 */
13 template <typename FEnd>
13 template <typename FEnd>
14 struct MergeScope {
14 struct MergeScope {
15 explicit MergeScope(FEnd end) : m_End{end} {}
15 explicit MergeScope(FEnd end) : m_End{end} {}
16 virtual ~MergeScope() noexcept { m_End(); }
16 virtual ~MergeScope() noexcept { m_End(); }
17 FEnd m_End;
17 FEnd m_End;
18 };
18 };
19
19
20 /**
20 /**
21 * Creates a scope for merge operation
21 * Creates a scope for merge operation
22 * @tparam end the function executed at the end of the scope
22 * @tparam end the function executed at the end of the scope
23 */
23 */
24 template <typename FEnd>
24 template <typename FEnd>
25 MergeScope<FEnd> scope(FEnd end)
25 MergeScope<FEnd> scope(FEnd end)
26 {
26 {
27 return MergeScope<FEnd>{end};
27 return MergeScope<FEnd>{end};
28 }
28 }
29
29
30 /**
31 * Enum used to position a data series relative to another during a merge operation
32 */
33 enum class MergePosition { LOWER_THAN, GREATER_THAN, EQUAL, OVERLAP };
34
35 /**
36 * Computes the position of the first data series relative to the second data series
37 * @param lhs the first data series
38 * @param rhs the second data series
39 * @return the merge position computed
40 * @remarks the data series must not be empty
41 */
42 template <int Dim>
43 MergePosition mergePosition(DataSeries<Dim> &lhs, DataSeries<Dim> &rhs)
44 {
45 Q_ASSERT(!lhs.isEmpty() && !rhs.isEmpty());
46
47 // Case lhs < rhs
48 auto lhsLast = --lhs.cend();
49 auto rhsFirst = rhs.cbegin();
50 if (lhsLast->x() < rhsFirst->x()) {
51 return MergePosition::LOWER_THAN;
52 }
53
54 // Case lhs > rhs
55 auto lhsFirst = lhs.cbegin();
56 auto rhsLast = --rhs.cend();
57 if (lhsFirst->x() > rhsLast->x()) {
58 return MergePosition::GREATER_THAN;
59 }
60
61 // Other cases
62 auto equal = std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(),
63 [](const auto &it1, const auto &it2) {
64 return it1.x() == it2.x() && it1.values() == it2.values();
65 });
66 return equal ? MergePosition::EQUAL : MergePosition::OVERLAP;
67 }
68
69 } // namespace detail
30 } // namespace detail
70
31
71
32
72 /// Helper used to merge two DataSeries
33 /// Helper used to merge two DataSeries
73 /// @sa DataSeries
34 /// @sa DataSeries
74 struct DataSeriesMergeHelper {
35 struct DataSeriesMergeHelper {
75 /// Merges the source data series into the dest data series. Data of the source data series are
36 /// Merges the source data series into the dest data series. Data of the source data series are
76 /// consumed
37 /// consumed
77 template <int Dim>
38 template <int Dim>
78 static void merge(DataSeries<Dim> &source, DataSeries<Dim> &dest)
39 static void merge(DataSeries<Dim> &source, DataSeries<Dim> &dest)
79 {
40 {
80 // Creates a scope to clear source data series at the end of the merge
41 // Creates a scope to clear source data series at the end of the merge
81 auto _ = detail::scope([&source]() { source.clear(); });
42 auto _ = detail::scope([&source]() { source.clear(); });
82
43
83 // Case : source data series is empty -> no merge is made
44 // Case : source data series is empty -> no merge is made
84 if (source.isEmpty()) {
45 if (source.isEmpty()) {
85 return;
46 return;
86 }
47 }
87
48
88 // Case : dest data series is empty -> we simply swap the data
49 // Case : dest data series is empty -> we simply swap the data
89 if (dest.isEmpty()) {
50 if (dest.isEmpty()) {
90 std::swap(dest.m_XAxisData, source.m_XAxisData);
51 std::swap(dest.m_XAxisData, source.m_XAxisData);
91 std::swap(dest.m_ValuesData, source.m_ValuesData);
52 std::swap(dest.m_ValuesData, source.m_ValuesData);
92 return;
53 return;
93 }
54 }
94
55
95 // Gets the position of the source in relation to the destination
56 auto destMin = dest.cbegin()->x();
96 auto sourcePosition = detail::mergePosition(source, dest);
57 auto destMax = (--dest.cend())->x();
58
59 auto sourceBegin = source.cbegin();
60 auto sourceEnd = source.cend();
61 auto sourceMin = sourceBegin->x();
62 auto sourceMax = (--source.cend())->x();
97
63
98 switch (sourcePosition) {
64 // Case : source bounds are inside dest bounds -> no merge is made
99 case detail::MergePosition::LOWER_THAN:
65 if (sourceMin >= destMin && sourceMax <= destMax) {
100 case detail::MergePosition::GREATER_THAN: {
66 return;
101 auto prepend = sourcePosition == detail::MergePosition::LOWER_THAN;
102 dest.m_XAxisData->add(*source.m_XAxisData, prepend);
103 dest.m_ValuesData->add(*source.m_ValuesData, prepend);
104 break;
105 }
106 case detail::MergePosition::EQUAL:
107 // the data series equal each other : no merge made
108 break;
109 case detail::MergePosition::OVERLAP: {
110 // the two data series overlap : merge is made
111 auto temp = dest.clone();
112 if (auto tempSeries = dynamic_cast<DataSeries<Dim> *>(temp.get())) {
113 // Makes the merge :
114 // - Data are sorted by x-axis values
115 // - If two entries are in the source range and the other range, only one entry
116 // is retained as result
117 // - The results are stored directly in the data series
118 dest.clear();
119 std::set_union(
120 tempSeries->cbegin(), tempSeries->cend(), source.cbegin(), source.cend(),
121 std::back_inserter(dest),
122 [](const auto &it1, const auto &it2) { return it1.x() < it2.x(); });
123 }
124 break;
125 }
126 default:
127 Q_ASSERT(false);
128 }
67 }
68
69 // Default case :
70 // - prepend to dest the values of source that are lower than min value of dest
71 // - append to dest the values of source that are greater than max value of dest
72 auto lowerIt
73 = std::lower_bound(sourceBegin, sourceEnd, destMin,
74 [](const auto &it, const auto &val) { return it.x() < val; });
75 auto upperIt
76 = std::upper_bound(lowerIt, sourceEnd, destMax,
77 [](const auto &val, const auto &it) { return val < it.x(); });
78 dest.insert(sourceBegin, lowerIt, true);
79 dest.insert(upperIt, sourceEnd);
129 }
80 }
130 };
81 };
131
82
132 #endif // SCIQLOP_DATASERIESMERGEHELPER_H
83 #endif // SCIQLOP_DATASERIESMERGEHELPER_H
@@ -1,27 +1,27
1 #ifndef SCIQLOP_SCALARSERIES_H
1 #ifndef SCIQLOP_SCALARSERIES_H
2 #define SCIQLOP_SCALARSERIES_H
2 #define SCIQLOP_SCALARSERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Data/DataSeries.h>
6 #include <Data/DataSeries.h>
7
7
8 /**
8 /**
9 * @brief The ScalarSeries class is the implementation for a data series representing a scalar.
9 * @brief The ScalarSeries class is the implementation for a data series representing a scalar.
10 */
10 */
11 class SCIQLOP_CORE_EXPORT ScalarSeries : public DataSeries<1> {
11 class SCIQLOP_CORE_EXPORT ScalarSeries : public DataSeries<1> {
12 public:
12 public:
13 /**
13 /**
14 * Ctor with two vectors. The vectors must have the same size, otherwise a ScalarSeries with no
14 * Ctor with two vectors. The vectors must have the same size, otherwise a ScalarSeries with no
15 * values will be created.
15 * values will be created.
16 * @param xAxisData x-axis data
16 * @param xAxisData x-axis data
17 * @param valuesData values data
17 * @param valuesData values data
18 */
18 */
19 explicit ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
19 explicit ScalarSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
20 const Unit &xAxisUnit, const Unit &valuesUnit);
20 const Unit &xAxisUnit, const Unit &valuesUnit);
21
21
22 std::unique_ptr<IDataSeries> clone() const override;
22 std::unique_ptr<IDataSeries> clone() const override;
23
23
24 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
24 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
25 };
25 };
26
26
27 #endif // SCIQLOP_SCALARSERIES_H
27 #endif // SCIQLOP_SCALARSERIES_H
@@ -1,110 +1,108
1 #ifndef SCIQLOP_SQPITERATOR_H
1 #ifndef SCIQLOP_SQPITERATOR_H
2 #define SCIQLOP_SQPITERATOR_H
2 #define SCIQLOP_SQPITERATOR_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 /**
6 /**
7 * @brief The SqpIterator class represents an iterator used in SciQlop. It defines all operators
7 * @brief The SqpIterator class represents an iterator used in SciQlop. It defines all operators
8 * needed for a standard forward iterator
8 * needed for a standard forward iterator
9 * @tparam T the type of object handled in iterator
9 * @tparam T the type of object handled in iterator
10 * @sa http://www.cplusplus.com/reference/iterator/
10 * @sa http://www.cplusplus.com/reference/iterator/
11 */
11 */
12 template <typename T>
12 template <typename T>
13 class SCIQLOP_CORE_EXPORT SqpIterator {
13 class SCIQLOP_CORE_EXPORT SqpIterator {
14 public:
14 public:
15 using iterator_category = std::random_access_iterator_tag;
15 using iterator_category = std::random_access_iterator_tag;
16 using value_type = const T;
16 using value_type = const T;
17 using difference_type = std::ptrdiff_t;
17 using difference_type = std::ptrdiff_t;
18 using pointer = value_type *;
18 using pointer = value_type *;
19 using reference = value_type &;
19 using reference = value_type &;
20
20
21 explicit SqpIterator(T value) : m_CurrentValue{std::move(value)} {}
21 explicit SqpIterator(T value) : m_CurrentValue{std::move(value)} {}
22
22
23 virtual ~SqpIterator() noexcept = default;
23 virtual ~SqpIterator() noexcept = default;
24 SqpIterator(const SqpIterator &) = default;
24 SqpIterator(const SqpIterator &) = default;
25 SqpIterator &operator=(SqpIterator other) { swap(m_CurrentValue, other.m_CurrentValue); }
25 SqpIterator &operator=(SqpIterator other) { swap(m_CurrentValue, other.m_CurrentValue); }
26
26
27 SqpIterator &operator++()
27 SqpIterator &operator++()
28 {
28 {
29 m_CurrentValue.next();
29 m_CurrentValue.next();
30 return *this;
30 return *this;
31 }
31 }
32
32
33 SqpIterator &operator--()
33 SqpIterator &operator--()
34 {
34 {
35 m_CurrentValue.prev();
35 m_CurrentValue.prev();
36 return *this;
36 return *this;
37 }
37 }
38
38
39 SqpIterator operator++(int)const
39 SqpIterator operator++(int)const
40 {
40 {
41 auto result = *this;
41 auto result = *this;
42 this->operator++();
42 this->operator++();
43 return result;
43 return result;
44 }
44 }
45 SqpIterator operator--(int)const
45 SqpIterator operator--(int)const
46 {
46 {
47 auto result = *this;
47 auto result = *this;
48 this->operator--();
48 this->operator--();
49 return result;
49 return result;
50 }
50 }
51
51
52 SqpIterator &operator+=(int offset)
52 SqpIterator &operator+=(int offset)
53 {
53 {
54 if (offset >= 0) {
54 if (offset >= 0) {
55 while (offset--) {
55 m_CurrentValue.next(offset);
56 m_CurrentValue.next();
57 }
58 }
56 }
59 else {
57 else {
60 while (offset++) {
58 while (offset++) {
61 m_CurrentValue.prev();
59 m_CurrentValue.prev();
62 }
60 }
63 }
61 }
64
62
65 return *this;
63 return *this;
66 }
64 }
67 SqpIterator &operator-=(int offset) { return *this += -offset; }
65 SqpIterator &operator-=(int offset) { return *this += -offset; }
68
66
69 SqpIterator operator+(int offset) const
67 SqpIterator operator+(int offset) const
70 {
68 {
71 auto result = *this;
69 auto result = *this;
72 result += offset;
70 result += offset;
73 return result;
71 return result;
74 }
72 }
75 SqpIterator operator-(int offset) const
73 SqpIterator operator-(int offset) const
76 {
74 {
77 auto result = *this;
75 auto result = *this;
78 result -= offset;
76 result -= offset;
79 return result;
77 return result;
80 }
78 }
81
79
82 int operator-(const SqpIterator &other) const
80 int operator-(const SqpIterator &other) const
83 {
81 {
84 return m_CurrentValue.distance(other.m_CurrentValue);
82 return m_CurrentValue.distance(other.m_CurrentValue);
85 }
83 }
86
84
87 const T *operator->() const { return &m_CurrentValue; }
85 const T *operator->() const { return &m_CurrentValue; }
88 const T &operator*() const { return m_CurrentValue; }
86 const T &operator*() const { return m_CurrentValue; }
89 T *operator->() { return &m_CurrentValue; }
87 T *operator->() { return &m_CurrentValue; }
90 T &operator*() { return m_CurrentValue; }
88 T &operator*() { return m_CurrentValue; }
91 T &operator[](int offset) const { return m_CurrentValue.advance(offset); }
89 T &operator[](int offset) const { return m_CurrentValue.advance(offset); }
92
90
93 bool operator==(const SqpIterator &other) const
91 bool operator==(const SqpIterator &other) const
94 {
92 {
95 return m_CurrentValue.equals(other.m_CurrentValue);
93 return m_CurrentValue.equals(other.m_CurrentValue);
96 }
94 }
97 bool operator!=(const SqpIterator &other) const { return !(*this == other); }
95 bool operator!=(const SqpIterator &other) const { return !(*this == other); }
98 bool operator>(const SqpIterator &other) const { return other.m_CurrentValue.lowerThan(*this); }
96 bool operator>(const SqpIterator &other) const { return other.m_CurrentValue.lowerThan(*this); }
99 bool operator<(const SqpIterator &other) const
97 bool operator<(const SqpIterator &other) const
100 {
98 {
101 return m_CurrentValue.lowerThan(other.m_CurrentValue);
99 return m_CurrentValue.lowerThan(other.m_CurrentValue);
102 }
100 }
103 bool operator>=(const SqpIterator &other) const { return !(*this < other); }
101 bool operator>=(const SqpIterator &other) const { return !(*this < other); }
104 bool operator<=(const SqpIterator &other) const { return !(*this > other); }
102 bool operator<=(const SqpIterator &other) const { return !(*this > other); }
105
103
106 private:
104 private:
107 T m_CurrentValue;
105 T m_CurrentValue;
108 };
106 };
109
107
110 #endif // SCIQLOP_SQPITERATOR_H
108 #endif // SCIQLOP_SQPITERATOR_H
@@ -1,34 +1,34
1 #ifndef SCIQLOP_VECTORSERIES_H
1 #ifndef SCIQLOP_VECTORSERIES_H
2 #define SCIQLOP_VECTORSERIES_H
2 #define SCIQLOP_VECTORSERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Data/DataSeries.h>
6 #include <Data/DataSeries.h>
7
7
8 /**
8 /**
9 * @brief The VectorSeries class is the implementation for a data series representing a vector.
9 * @brief The VectorSeries class is the implementation for a data series representing a vector.
10 */
10 */
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
11 class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> {
12 public:
12 public:
13 /**
13 /**
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
15 * ScalarSeries with no values will be created.
15 * ScalarSeries with no values will be created.
16 * @param xAxisData x-axis data
16 * @param xAxisData x-axis data
17 * @param xvaluesData x-values data
17 * @param xvaluesData x-values data
18 * @param yvaluesData y-values data
18 * @param yvaluesData y-values data
19 * @param zvaluesData z-values data
19 * @param zvaluesData z-values data
20 */
20 */
21 explicit VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
21 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> xValuesData,
22 QVector<double> yValuesData, QVector<double> zValuesData,
22 std::vector<double> yValuesData, std::vector<double> zValuesData,
23 const Unit &xAxisUnit, const Unit &valuesUnit);
23 const Unit &xAxisUnit, const Unit &valuesUnit);
24
24
25 /// Default Ctor
25 /// Default Ctor
26 explicit VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
26 explicit VectorSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
27 const Unit &xAxisUnit, const Unit &valuesUnit);
27 const Unit &xAxisUnit, const Unit &valuesUnit);
28
28
29 std::unique_ptr<IDataSeries> clone() const;
29 std::unique_ptr<IDataSeries> clone() const;
30
30
31 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
31 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
32 };
32 };
33
33
34 #endif // SCIQLOP_VECTORSERIES_H
34 #endif // SCIQLOP_VECTORSERIES_H
@@ -1,77 +1,77
1 #include "Data/ArrayDataIterator.h"
1 #include "Data/ArrayDataIterator.h"
2
2
3 ArrayDataIteratorValue::ArrayDataIteratorValue(std::unique_ptr<ArrayDataIteratorValue::Impl> impl)
3 ArrayDataIteratorValue::ArrayDataIteratorValue(std::unique_ptr<ArrayDataIteratorValue::Impl> impl)
4 : m_Impl{std::move(impl)}
4 : m_Impl{std::move(impl)}
5 {
5 {
6 }
6 }
7
7
8 ArrayDataIteratorValue::ArrayDataIteratorValue(const ArrayDataIteratorValue &other)
8 ArrayDataIteratorValue::ArrayDataIteratorValue(const ArrayDataIteratorValue &other)
9 : m_Impl{other.m_Impl->clone()}
9 : m_Impl{other.m_Impl->clone()}
10 {
10 {
11 }
11 }
12
12
13 ArrayDataIteratorValue &ArrayDataIteratorValue::operator=(ArrayDataIteratorValue other)
13 ArrayDataIteratorValue &ArrayDataIteratorValue::operator=(ArrayDataIteratorValue other)
14 {
14 {
15 m_Impl->swap(*other.m_Impl);
15 m_Impl->swap(*other.m_Impl);
16 return *this;
16 return *this;
17 }
17 }
18
18
19 int ArrayDataIteratorValue::distance(const ArrayDataIteratorValue &other) const
19 int ArrayDataIteratorValue::distance(const ArrayDataIteratorValue &other) const
20 {
20 {
21 return m_Impl->distance(*other.m_Impl);
21 return m_Impl->distance(*other.m_Impl);
22 }
22 }
23
23
24 bool ArrayDataIteratorValue::equals(const ArrayDataIteratorValue &other) const
24 bool ArrayDataIteratorValue::equals(const ArrayDataIteratorValue &other) const
25 {
25 {
26 return m_Impl->equals(*other.m_Impl);
26 return m_Impl->equals(*other.m_Impl);
27 }
27 }
28
28
29 bool ArrayDataIteratorValue::lowerThan(const ArrayDataIteratorValue &other) const
29 bool ArrayDataIteratorValue::lowerThan(const ArrayDataIteratorValue &other) const
30 {
30 {
31 return m_Impl->lowerThan(*other.m_Impl);
31 return m_Impl->lowerThan(*other.m_Impl);
32 }
32 }
33
33
34 ArrayDataIteratorValue ArrayDataIteratorValue::advance(int offset) const
34 ArrayDataIteratorValue ArrayDataIteratorValue::advance(int offset) const
35 {
35 {
36 return ArrayDataIteratorValue{m_Impl->advance(offset)};
36 return ArrayDataIteratorValue{m_Impl->advance(offset)};
37 }
37 }
38
38
39 void ArrayDataIteratorValue::next()
39 void ArrayDataIteratorValue::next(int offset)
40 {
40 {
41 m_Impl->next();
41 m_Impl->next(offset);
42 }
42 }
43
43
44 void ArrayDataIteratorValue::prev()
44 void ArrayDataIteratorValue::prev()
45 {
45 {
46 m_Impl->prev();
46 m_Impl->prev();
47 }
47 }
48
48
49 double ArrayDataIteratorValue::at(int componentIndex) const
49 double ArrayDataIteratorValue::at(int componentIndex) const
50 {
50 {
51 return m_Impl->at(componentIndex);
51 return m_Impl->at(componentIndex);
52 }
52 }
53
53
54 double ArrayDataIteratorValue::first() const
54 double ArrayDataIteratorValue::first() const
55 {
55 {
56 return m_Impl->first();
56 return m_Impl->first();
57 }
57 }
58
58
59 double ArrayDataIteratorValue::min() const
59 double ArrayDataIteratorValue::min() const
60 {
60 {
61 return m_Impl->min();
61 return m_Impl->min();
62 }
62 }
63
63
64 double ArrayDataIteratorValue::max() const
64 double ArrayDataIteratorValue::max() const
65 {
65 {
66 return m_Impl->max();
66 return m_Impl->max();
67 }
67 }
68
68
69 QVector<double> ArrayDataIteratorValue::values() const
69 QVector<double> ArrayDataIteratorValue::values() const
70 {
70 {
71 return m_Impl->values();
71 return m_Impl->values();
72 }
72 }
73
73
74 ArrayDataIteratorValue::Impl *ArrayDataIteratorValue::impl()
74 ArrayDataIteratorValue::Impl *ArrayDataIteratorValue::impl()
75 {
75 {
76 return m_Impl.get();
76 return m_Impl.get();
77 }
77 }
@@ -1,84 +1,84
1 #include "Data/DataSeriesIterator.h"
1 #include "Data/DataSeriesIterator.h"
2
2
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
3 DataSeriesIteratorValue::DataSeriesIteratorValue(
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
4 std::unique_ptr<DataSeriesIteratorValue::Impl> impl)
5 : m_Impl{std::move(impl)}
5 : m_Impl{std::move(impl)}
6 {
6 {
7 }
7 }
8
8
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
9 DataSeriesIteratorValue::DataSeriesIteratorValue(const DataSeriesIteratorValue &other)
10 : m_Impl{other.m_Impl->clone()}
10 : m_Impl{other.m_Impl->clone()}
11 {
11 {
12 }
12 }
13
13
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
14 DataSeriesIteratorValue &DataSeriesIteratorValue::operator=(DataSeriesIteratorValue other)
15 {
15 {
16 m_Impl->swap(*other.m_Impl);
16 m_Impl->swap(*other.m_Impl);
17 return *this;
17 return *this;
18 }
18 }
19
19
20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
20 int DataSeriesIteratorValue::distance(const DataSeriesIteratorValue &other) const
21 {
21 {
22 auto dist = m_Impl->distance(*other.m_Impl);
22 auto dist = m_Impl->distance(*other.m_Impl);
23 return m_Impl->distance(*other.m_Impl);
23 return m_Impl->distance(*other.m_Impl);
24 }
24 }
25
25
26 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
26 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
27 {
27 {
28 return m_Impl->equals(*other.m_Impl);
28 return m_Impl->equals(*other.m_Impl);
29 }
29 }
30
30
31 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
31 bool DataSeriesIteratorValue::lowerThan(const DataSeriesIteratorValue &other) const
32 {
32 {
33 return m_Impl->lowerThan(*other.m_Impl);
33 return m_Impl->lowerThan(*other.m_Impl);
34 }
34 }
35
35
36 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
36 DataSeriesIteratorValue DataSeriesIteratorValue::advance(int offset) const
37 {
37 {
38 return DataSeriesIteratorValue{m_Impl->advance(offset)};
38 return DataSeriesIteratorValue{m_Impl->advance(offset)};
39 }
39 }
40
40
41 void DataSeriesIteratorValue::next()
41 void DataSeriesIteratorValue::next(int offset)
42 {
42 {
43 m_Impl->next();
43 m_Impl->next(offset);
44 }
44 }
45
45
46 void DataSeriesIteratorValue::prev()
46 void DataSeriesIteratorValue::prev()
47 {
47 {
48 m_Impl->prev();
48 m_Impl->prev();
49 }
49 }
50
50
51 double DataSeriesIteratorValue::x() const
51 double DataSeriesIteratorValue::x() const
52 {
52 {
53 return m_Impl->x();
53 return m_Impl->x();
54 }
54 }
55
55
56 double DataSeriesIteratorValue::value() const
56 double DataSeriesIteratorValue::value() const
57 {
57 {
58 return m_Impl->value();
58 return m_Impl->value();
59 }
59 }
60
60
61 double DataSeriesIteratorValue::value(int componentIndex) const
61 double DataSeriesIteratorValue::value(int componentIndex) const
62 {
62 {
63 return m_Impl->value(componentIndex);
63 return m_Impl->value(componentIndex);
64 }
64 }
65
65
66 double DataSeriesIteratorValue::minValue() const
66 double DataSeriesIteratorValue::minValue() const
67 {
67 {
68 return m_Impl->minValue();
68 return m_Impl->minValue();
69 }
69 }
70
70
71 double DataSeriesIteratorValue::maxValue() const
71 double DataSeriesIteratorValue::maxValue() const
72 {
72 {
73 return m_Impl->maxValue();
73 return m_Impl->maxValue();
74 }
74 }
75
75
76 QVector<double> DataSeriesIteratorValue::values() const
76 QVector<double> DataSeriesIteratorValue::values() const
77 {
77 {
78 return m_Impl->values();
78 return m_Impl->values();
79 }
79 }
80
80
81 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
81 DataSeriesIteratorValue::Impl *DataSeriesIteratorValue::impl()
82 {
82 {
83 return m_Impl.get();
83 return m_Impl.get();
84 }
84 }
@@ -1,31 +1,31
1 #include <Data/ScalarSeries.h>
1 #include <Data/ScalarSeries.h>
2
2
3 ScalarSeries::ScalarSeries(QVector<double> xAxisData, QVector<double> valuesData,
3 ScalarSeries::ScalarSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
4 const Unit &xAxisUnit, const Unit &valuesUnit)
4 const Unit &xAxisUnit, const Unit &valuesUnit)
5 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
5 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
6 std::make_shared<ArrayData<1> >(std::move(valuesData)), valuesUnit}
6 std::make_shared<ArrayData<1> >(std::move(valuesData)), valuesUnit}
7 {
7 {
8 }
8 }
9
9
10 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
10 std::unique_ptr<IDataSeries> ScalarSeries::clone() const
11 {
11 {
12 return std::make_unique<ScalarSeries>(*this);
12 return std::make_unique<ScalarSeries>(*this);
13 }
13 }
14
14
15 std::shared_ptr<IDataSeries> ScalarSeries::subDataSeries(const SqpRange &range)
15 std::shared_ptr<IDataSeries> ScalarSeries::subDataSeries(const SqpRange &range)
16 {
16 {
17 auto subXAxisData = QVector<double>();
17 auto subXAxisData = std::vector<double>();
18 auto subValuesData = QVector<double>();
18 auto subValuesData = std::vector<double>();
19 this->lockRead();
19 this->lockRead();
20 {
20 {
21 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
21 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
22 for (auto it = bounds.first; it != bounds.second; ++it) {
22 for (auto it = bounds.first; it != bounds.second; ++it) {
23 subXAxisData.append(it->x());
23 subXAxisData.push_back(it->x());
24 subValuesData.append(it->value());
24 subValuesData.push_back(it->value());
25 }
25 }
26 }
26 }
27 this->unlock();
27 this->unlock();
28
28
29 return std::make_shared<ScalarSeries>(subXAxisData, subValuesData, this->xAxisUnit(),
29 return std::make_shared<ScalarSeries>(std::move(subXAxisData), std::move(subValuesData),
30 this->valuesUnit());
30 this->xAxisUnit(), this->valuesUnit());
31 }
31 }
@@ -1,83 +1,88
1 #include "Data/VectorSeries.h"
1 #include "Data/VectorSeries.h"
2
2
3 namespace {
3 namespace {
4
4
5 /**
5 /**
6 * Flatten the three components of a vector to a single QVector that can be passed to an ArrayData
6 * Flatten the three components of a vector to a single QVector that can be passed to an ArrayData
7 *
7 *
8 * Example:
8 * Example:
9 * xValues = {1, 2, 3}
9 * xValues = {1, 2, 3}
10 * yValues = {4, 5, 6}
10 * yValues = {4, 5, 6}
11 * zValues = {7, 8, 9}
11 * zValues = {7, 8, 9}
12 *
12 *
13 * result = {1, 4, 7, 2, 5, 8, 3, 6, 9}
13 * result = {1, 4, 7, 2, 5, 8, 3, 6, 9}
14 *
14 *
15 * @param xValues the x-component values of the vector
15 * @param xValues the x-component values of the vector
16 * @param yValues the y-component values of the vector
16 * @param yValues the y-component values of the vector
17 * @param zValues the z-component values of the vector
17 * @param zValues the z-component values of the vector
18 * @return the single QVector
18 * @return the single QVector
19 * @remarks the three components are consumed
19 * @remarks the three components are consumed
20 * @sa ArrayData
20 * @sa ArrayData
21 */
21 */
22 QVector<double> flatten(QVector<double> xValues, QVector<double> yValues, QVector<double> zValues)
22 std::vector<double> flatten(std::vector<double> xValues, std::vector<double> yValues,
23 std::vector<double> zValues)
23 {
24 {
24 if (xValues.size() != yValues.size() || xValues.size() != zValues.size()) {
25 if (xValues.size() != yValues.size() || xValues.size() != zValues.size()) {
25 /// @todo ALX : log
26 /// @todo ALX : log
26 return {};
27 return {};
27 }
28 }
28
29
29 auto result = QVector<double>{};
30 auto result = std::vector<double>();
30 result.reserve(xValues.size() * 3);
31 result.reserve(xValues.size() * 3);
31
32
32 while (!xValues.isEmpty()) {
33 while (!xValues.empty()) {
33 result.append({xValues.takeFirst(), yValues.takeFirst(), zValues.takeFirst()});
34 result.insert(result.cend(), {xValues.front(), yValues.front(), zValues.front()});
35 xValues.erase(xValues.begin());
36 yValues.erase(yValues.begin());
37 zValues.erase(zValues.begin());
34 }
38 }
35
39
36 return result;
40 return result;
37 }
41 }
38
42
39 } // namespace
43 } // namespace
40
44
41 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
45 VectorSeries::VectorSeries(std::vector<double> xAxisData, std::vector<double> xValuesData,
42 QVector<double> yValuesData, QVector<double> zValuesData,
46 std::vector<double> yValuesData, std::vector<double> zValuesData,
43 const Unit &xAxisUnit, const Unit &valuesUnit)
47 const Unit &xAxisUnit, const Unit &valuesUnit)
44 : VectorSeries{std::move(xAxisData), flatten(std::move(xValuesData), std::move(yValuesData),
48 : VectorSeries{std::move(xAxisData), flatten(std::move(xValuesData), std::move(yValuesData),
45 std::move(zValuesData)),
49 std::move(zValuesData)),
46 xAxisUnit, valuesUnit}
50 xAxisUnit, valuesUnit}
47 {
51 {
48 }
52 }
49
53
50 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
54 VectorSeries::VectorSeries(std::vector<double> xAxisData, std::vector<double> valuesData,
51 const Unit &xAxisUnit, const Unit &valuesUnit)
55 const Unit &xAxisUnit, const Unit &valuesUnit)
52 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
56 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
53 std::make_shared<ArrayData<2> >(std::move(valuesData), 3), valuesUnit}
57 std::make_shared<ArrayData<2> >(std::move(valuesData), 3), valuesUnit}
54 {
58 {
55 }
59 }
56
60
57 std::unique_ptr<IDataSeries> VectorSeries::clone() const
61 std::unique_ptr<IDataSeries> VectorSeries::clone() const
58 {
62 {
59 return std::make_unique<VectorSeries>(*this);
63 return std::make_unique<VectorSeries>(*this);
60 }
64 }
61
65
62 std::shared_ptr<IDataSeries> VectorSeries::subDataSeries(const SqpRange &range)
66 std::shared_ptr<IDataSeries> VectorSeries::subDataSeries(const SqpRange &range)
63 {
67 {
64 auto subXAxisData = QVector<double>();
68 auto subXAxisData = std::vector<double>();
65 auto subXValuesData = QVector<double>();
69 auto subXValuesData = std::vector<double>();
66 auto subYValuesData = QVector<double>();
70 auto subYValuesData = std::vector<double>();
67 auto subZValuesData = QVector<double>();
71 auto subZValuesData = std::vector<double>();
68
72
69 this->lockRead();
73 this->lockRead();
70 {
74 {
71 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
75 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
72 for (auto it = bounds.first; it != bounds.second; ++it) {
76 for (auto it = bounds.first; it != bounds.second; ++it) {
73 subXAxisData.append(it->x());
77 subXAxisData.push_back(it->x());
74 subXValuesData.append(it->value(0));
78 subXValuesData.push_back(it->value(0));
75 subYValuesData.append(it->value(1));
79 subYValuesData.push_back(it->value(1));
76 subZValuesData.append(it->value(2));
80 subZValuesData.push_back(it->value(2));
77 }
81 }
78 }
82 }
79 this->unlock();
83 this->unlock();
80
84
81 return std::make_shared<VectorSeries>(subXAxisData, subXValuesData, subYValuesData,
85 return std::make_shared<VectorSeries>(std::move(subXAxisData), std::move(subXValuesData),
82 subZValuesData, this->xAxisUnit(), this->valuesUnit());
86 std::move(subYValuesData), std::move(subZValuesData),
87 this->xAxisUnit(), this->valuesUnit());
83 }
88 }
@@ -1,638 +1,636
1 #include "Data/DataSeries.h"
1 #include "Data/DataSeries.h"
2 #include "Data/ScalarSeries.h"
2 #include "Data/ScalarSeries.h"
3 #include "Data/VectorSeries.h"
3 #include "Data/VectorSeries.h"
4
4
5 #include <cmath>
5 #include <cmath>
6
6
7 #include <QObject>
7 #include <QObject>
8 #include <QtTest>
8 #include <QtTest>
9
9
10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12
12
13 namespace {
13 namespace {
14
14
15 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const QVector<double> &xData,
15 using DataContainer = std::vector<double>;
16 const QVector<double> &valuesData)
16
17 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
18 const DataContainer &valuesData)
17 {
19 {
18 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
20 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
19 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
21 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
20 QVERIFY(std::equal(
22 QVERIFY(std::equal(
21 first, last, valuesData.cbegin(), valuesData.cend(),
23 first, last, valuesData.cbegin(), valuesData.cend(),
22 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
24 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
23 }
25 }
24
26
25 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const QVector<double> &xData,
27 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
26 const QVector<QVector<double> > &valuesData)
28 const std::vector<DataContainer> &valuesData)
27 {
29 {
28 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
30 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
29 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
31 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
30 for (auto i = 0; i < valuesData.size(); ++i) {
32 for (auto i = 0; i < valuesData.size(); ++i) {
31 auto componentData = valuesData.at(i);
33 auto componentData = valuesData.at(i);
32
34
33 QVERIFY(std::equal(
35 QVERIFY(std::equal(
34 first, last, componentData.cbegin(), componentData.cend(),
36 first, last, componentData.cbegin(), componentData.cend(),
35 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
37 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
36 }
38 }
37 }
39 }
38
40
39 } // namespace
41 } // namespace
40
42
41 class TestDataSeries : public QObject {
43 class TestDataSeries : public QObject {
42 Q_OBJECT
44 Q_OBJECT
43 private:
45 private:
44 template <typename T>
46 template <typename T>
45 void testValuesBoundsStructure()
47 void testValuesBoundsStructure()
46 {
48 {
47 // ////////////// //
49 // ////////////// //
48 // Test structure //
50 // Test structure //
49 // ////////////// //
51 // ////////////// //
50
52
51 // Data series to get values bounds
53 // Data series to get values bounds
52 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
54 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
53
55
54 // x-axis range
56 // x-axis range
55 QTest::addColumn<double>("minXAxis");
57 QTest::addColumn<double>("minXAxis");
56 QTest::addColumn<double>("maxXAxis");
58 QTest::addColumn<double>("maxXAxis");
57
59
58 // Expected results
60 // Expected results
59 QTest::addColumn<bool>(
61 QTest::addColumn<bool>(
60 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
62 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
61 QTest::addColumn<double>("expectedMinValue");
63 QTest::addColumn<double>("expectedMinValue");
62 QTest::addColumn<double>("expectedMaxValue");
64 QTest::addColumn<double>("expectedMaxValue");
63 }
65 }
64
66
65 template <typename T>
67 template <typename T>
66 void testValuesBounds()
68 void testValuesBounds()
67 {
69 {
68 QFETCH(std::shared_ptr<T>, dataSeries);
70 QFETCH(std::shared_ptr<T>, dataSeries);
69 QFETCH(double, minXAxis);
71 QFETCH(double, minXAxis);
70 QFETCH(double, maxXAxis);
72 QFETCH(double, maxXAxis);
71
73
72 QFETCH(bool, expectedOK);
74 QFETCH(bool, expectedOK);
73 QFETCH(double, expectedMinValue);
75 QFETCH(double, expectedMinValue);
74 QFETCH(double, expectedMaxValue);
76 QFETCH(double, expectedMaxValue);
75
77
76 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
78 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
77 auto end = dataSeries->cend();
79 auto end = dataSeries->cend();
78
80
79 // Checks iterators with expected result
81 // Checks iterators with expected result
80 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
82 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
81
83
82 if (expectedOK) {
84 if (expectedOK) {
83 auto compare = [](const auto &v1, const auto &v2) {
85 auto compare = [](const auto &v1, const auto &v2) {
84 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
86 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
85 };
87 };
86
88
87 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
89 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
88 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
90 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
89 }
91 }
90 }
92 }
91
93
92 template <typename T>
94 template <typename T>
93 void testPurgeStructure()
95 void testPurgeStructure()
94 {
96 {
95 // ////////////// //
97 // ////////////// //
96 // Test structure //
98 // Test structure //
97 // ////////////// //
99 // ////////////// //
98
100
99 // Data series to purge
101 // Data series to purge
100 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
102 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
101 QTest::addColumn<double>("min");
103 QTest::addColumn<double>("min");
102 QTest::addColumn<double>("max");
104 QTest::addColumn<double>("max");
103
105
104 // Expected values after purge
106 // Expected values after purge
105 QTest::addColumn<QVector<double> >("expectedXAxisData");
107 QTest::addColumn<DataContainer>("expectedXAxisData");
106 QTest::addColumn<QVector<QVector<double> > >("expectedValuesData");
108 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
107 }
109 }
108
110
109 template <typename T>
111 template <typename T>
110 void testPurge()
112 void testPurge()
111 {
113 {
112 QFETCH(std::shared_ptr<T>, dataSeries);
114 QFETCH(std::shared_ptr<T>, dataSeries);
113 QFETCH(double, min);
115 QFETCH(double, min);
114 QFETCH(double, max);
116 QFETCH(double, max);
115
117
116 dataSeries->purge(min, max);
118 dataSeries->purge(min, max);
117
119
118 // Validates results
120 // Validates results
119 QFETCH(QVector<double>, expectedXAxisData);
121 QFETCH(DataContainer, expectedXAxisData);
120 QFETCH(QVector<QVector<double> >, expectedValuesData);
122 QFETCH(std::vector<DataContainer>, expectedValuesData);
121
123
122 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
124 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
123 expectedValuesData);
125 expectedValuesData);
124 }
126 }
125
127
126 private slots:
128 private slots:
127
129
128 /// Input test data
130 /// Input test data
129 /// @sa testCtor()
131 /// @sa testCtor()
130 void testCtor_data();
132 void testCtor_data();
131
133
132 /// Tests construction of a data series
134 /// Tests construction of a data series
133 void testCtor();
135 void testCtor();
134
136
135 /// Input test data
137 /// Input test data
136 /// @sa testMerge()
138 /// @sa testMerge()
137 void testMerge_data();
139 void testMerge_data();
138
140
139 /// Tests merge of two data series
141 /// Tests merge of two data series
140 void testMerge();
142 void testMerge();
141
143
142 /// Input test data
144 /// Input test data
143 /// @sa testPurgeScalar()
145 /// @sa testPurgeScalar()
144 void testPurgeScalar_data();
146 void testPurgeScalar_data();
145
147
146 /// Tests purge of a scalar series
148 /// Tests purge of a scalar series
147 void testPurgeScalar();
149 void testPurgeScalar();
148
150
149 /// Input test data
151 /// Input test data
150 /// @sa testPurgeVector()
152 /// @sa testPurgeVector()
151 void testPurgeVector_data();
153 void testPurgeVector_data();
152
154
153 /// Tests purge of a vector series
155 /// Tests purge of a vector series
154 void testPurgeVector();
156 void testPurgeVector();
155
157
156 /// Input test data
158 /// Input test data
157 /// @sa testMinXAxisData()
159 /// @sa testMinXAxisData()
158 void testMinXAxisData_data();
160 void testMinXAxisData_data();
159
161
160 /// Tests get min x-axis data of a data series
162 /// Tests get min x-axis data of a data series
161 void testMinXAxisData();
163 void testMinXAxisData();
162
164
163 /// Input test data
165 /// Input test data
164 /// @sa testMaxXAxisData()
166 /// @sa testMaxXAxisData()
165 void testMaxXAxisData_data();
167 void testMaxXAxisData_data();
166
168
167 /// Tests get max x-axis data of a data series
169 /// Tests get max x-axis data of a data series
168 void testMaxXAxisData();
170 void testMaxXAxisData();
169
171
170 /// Input test data
172 /// Input test data
171 /// @sa testXAxisRange()
173 /// @sa testXAxisRange()
172 void testXAxisRange_data();
174 void testXAxisRange_data();
173
175
174 /// Tests get x-axis range of a data series
176 /// Tests get x-axis range of a data series
175 void testXAxisRange();
177 void testXAxisRange();
176
178
177 /// Input test data
179 /// Input test data
178 /// @sa testValuesBoundsScalar()
180 /// @sa testValuesBoundsScalar()
179 void testValuesBoundsScalar_data();
181 void testValuesBoundsScalar_data();
180
182
181 /// Tests get values bounds of a scalar series
183 /// Tests get values bounds of a scalar series
182 void testValuesBoundsScalar();
184 void testValuesBoundsScalar();
183
185
184 /// Input test data
186 /// Input test data
185 /// @sa testValuesBoundsVector()
187 /// @sa testValuesBoundsVector()
186 void testValuesBoundsVector_data();
188 void testValuesBoundsVector_data();
187
189
188 /// Tests get values bounds of a vector series
190 /// Tests get values bounds of a vector series
189 void testValuesBoundsVector();
191 void testValuesBoundsVector();
190 };
192 };
191
193
192 void TestDataSeries::testCtor_data()
194 void TestDataSeries::testCtor_data()
193 {
195 {
194 // ////////////// //
196 // ////////////// //
195 // Test structure //
197 // Test structure //
196 // ////////////// //
198 // ////////////// //
197
199
198 // x-axis data
200 // x-axis data
199 QTest::addColumn<QVector<double> >("xAxisData");
201 QTest::addColumn<DataContainer>("xAxisData");
200 // values data
202 // values data
201 QTest::addColumn<QVector<double> >("valuesData");
203 QTest::addColumn<DataContainer>("valuesData");
202
204
203 // expected x-axis data
205 // expected x-axis data
204 QTest::addColumn<QVector<double> >("expectedXAxisData");
206 QTest::addColumn<DataContainer>("expectedXAxisData");
205 // expected values data
207 // expected values data
206 QTest::addColumn<QVector<double> >("expectedValuesData");
208 QTest::addColumn<DataContainer>("expectedValuesData");
207
209
208 // ////////// //
210 // ////////// //
209 // Test cases //
211 // Test cases //
210 // ////////// //
212 // ////////// //
211
213
212 QTest::newRow("invalidData (different sizes of vectors)")
214 QTest::newRow("invalidData (different sizes of vectors)")
213 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
215 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << DataContainer{}
214 << QVector<double>{} << QVector<double>{};
216 << DataContainer{};
215
217
216 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
218 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
217 << QVector<double>{100., 200., 300., 400., 500.}
219 << DataContainer{100., 200., 300., 400., 500.}
218 << QVector<double>{1., 2., 3., 4., 5.}
220 << DataContainer{1., 2., 3., 4., 5.}
219 << QVector<double>{100., 200., 300., 400., 500.};
221 << DataContainer{100., 200., 300., 400., 500.};
220
222
221 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
223 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
222 << QVector<double>{100., 200., 300., 400., 500.}
224 << DataContainer{100., 200., 300., 400., 500.}
223 << QVector<double>{1., 2., 3., 4., 5.}
225 << DataContainer{1., 2., 3., 4., 5.}
224 << QVector<double>{500., 400., 300., 200., 100.};
226 << DataContainer{500., 400., 300., 200., 100.};
225
227
226 QTest::newRow("unsortedData2")
228 QTest::newRow("unsortedData2")
227 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
229 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.}
228 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
230 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
229 }
231 }
230
232
231 void TestDataSeries::testCtor()
233 void TestDataSeries::testCtor()
232 {
234 {
233 // Creates series
235 // Creates series
234 QFETCH(QVector<double>, xAxisData);
236 QFETCH(DataContainer, xAxisData);
235 QFETCH(QVector<double>, valuesData);
237 QFETCH(DataContainer, valuesData);
236
238
237 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
239 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
238 Unit{}, Unit{});
240 Unit{}, Unit{});
239
241
240 // Validates results : we check that the data series is sorted on its x-axis data
242 // Validates results : we check that the data series is sorted on its x-axis data
241 QFETCH(QVector<double>, expectedXAxisData);
243 QFETCH(DataContainer, expectedXAxisData);
242 QFETCH(QVector<double>, expectedValuesData);
244 QFETCH(DataContainer, expectedValuesData);
243
245
244 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
246 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
245 }
247 }
246
248
247 namespace {
249 namespace {
248
250
249 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
251 std::shared_ptr<ScalarSeries> createScalarSeries(DataContainer xAxisData, DataContainer valuesData)
250 QVector<double> valuesData)
251 {
252 {
252 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
253 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
253 Unit{});
254 Unit{});
254 }
255 }
255
256
256 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
257 std::shared_ptr<VectorSeries> createVectorSeries(DataContainer xAxisData, DataContainer xValuesData,
257 QVector<double> xValuesData,
258 DataContainer yValuesData,
258 QVector<double> yValuesData,
259 DataContainer zValuesData)
259 QVector<double> zValuesData)
260 {
260 {
261 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
261 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
262 std::move(yValuesData), std::move(zValuesData), Unit{},
262 std::move(yValuesData), std::move(zValuesData), Unit{},
263 Unit{});
263 Unit{});
264 }
264 }
265
265
266 } // namespace
266 } // namespace
267
267
268 void TestDataSeries::testMerge_data()
268 void TestDataSeries::testMerge_data()
269 {
269 {
270 // ////////////// //
270 // ////////////// //
271 // Test structure //
271 // Test structure //
272 // ////////////// //
272 // ////////////// //
273
273
274 // Data series to merge
274 // Data series to merge
275 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
275 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
276 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
276 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
277
277
278 // Expected values in the first data series after merge
278 // Expected values in the first data series after merge
279 QTest::addColumn<QVector<double> >("expectedXAxisData");
279 QTest::addColumn<DataContainer>("expectedXAxisData");
280 QTest::addColumn<QVector<double> >("expectedValuesData");
280 QTest::addColumn<DataContainer>("expectedValuesData");
281
281
282 // ////////// //
282 // ////////// //
283 // Test cases //
283 // Test cases //
284 // ////////// //
284 // ////////// //
285
285
286 QTest::newRow("sortedMerge")
286 QTest::newRow("sortedMerge")
287 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
287 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
288 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
288 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
289 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
289 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
290 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
290 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
291
291
292 QTest::newRow("unsortedMerge")
292 QTest::newRow("unsortedMerge")
293 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
293 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
294 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
294 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
295 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
295 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
296 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
296 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
297
297
298 QTest::newRow("unsortedMerge2")
298 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
299 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
299 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 800., 900., 1000.})
300 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
300 << createScalarSeries({3., 4., 5., 6., 7.}, {300., 400., 500., 600., 700.})
301 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
301 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
302 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
303
302
304 QTest::newRow("unsortedMerge3")
303 QTest::newRow("unsortedMerge3")
305 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
304 << createScalarSeries({3., 4., 5., 7., 8}, {300., 400., 500., 700., 800.})
306 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
305 << createScalarSeries({1., 2., 3., 7., 10.}, {100., 200., 333., 777., 1000.})
307 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
306 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
308 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
307 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
309 }
308 }
310
309
311 void TestDataSeries::testMerge()
310 void TestDataSeries::testMerge()
312 {
311 {
313 // Merges series
312 // Merges series
314 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
313 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
315 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
314 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
316
315
317 dataSeries->merge(dataSeries2.get());
316 dataSeries->merge(dataSeries2.get());
318
317
319 // Validates results : we check that the merge is valid and the data series is sorted on its
318 // Validates results : we check that the merge is valid and the data series is sorted on its
320 // x-axis data
319 // x-axis data
321 QFETCH(QVector<double>, expectedXAxisData);
320 QFETCH(DataContainer, expectedXAxisData);
322 QFETCH(QVector<double>, expectedValuesData);
321 QFETCH(DataContainer, expectedValuesData);
323
322
324 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
323 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
325 }
324 }
326
325
327 void TestDataSeries::testPurgeScalar_data()
326 void TestDataSeries::testPurgeScalar_data()
328 {
327 {
329 testPurgeStructure<ScalarSeries>();
328 testPurgeStructure<ScalarSeries>();
330
329
331 // ////////// //
330 // ////////// //
332 // Test cases //
331 // Test cases //
333 // ////////// //
332 // ////////// //
334
333
335 QTest::newRow("purgeScalar") << createScalarSeries({1., 2., 3., 4., 5.},
334 QTest::newRow("purgeScalar") << createScalarSeries({1., 2., 3., 4., 5.},
336 {100., 200., 300., 400., 500.})
335 {100., 200., 300., 400., 500.})
337 << 2. << 4. << QVector<double>{2., 3., 4.}
336 << 2. << 4. << DataContainer{2., 3., 4.}
338 << QVector<QVector<double> >{{200., 300., 400.}};
337 << std::vector<DataContainer>{{200., 300., 400.}};
339 QTest::newRow("purgeScalar2") << createScalarSeries({1., 2., 3., 4., 5.},
338 QTest::newRow("purgeScalar2") << createScalarSeries({1., 2., 3., 4., 5.},
340 {100., 200., 300., 400., 500.})
339 {100., 200., 300., 400., 500.})
341 << 0. << 2.5 << QVector<double>{1., 2.}
340 << 0. << 2.5 << DataContainer{1., 2.}
342 << QVector<QVector<double> >{{100., 200.}};
341 << std::vector<DataContainer>{{100., 200.}};
343 QTest::newRow("purgeScalar3") << createScalarSeries({1., 2., 3., 4., 5.},
342 QTest::newRow("purgeScalar3") << createScalarSeries({1., 2., 3., 4., 5.},
344 {100., 200., 300., 400., 500.})
343 {100., 200., 300., 400., 500.})
345 << 3.5 << 7. << QVector<double>{4., 5.}
344 << 3.5 << 7. << DataContainer{4., 5.}
346 << QVector<QVector<double> >{{400., 500.}};
345 << std::vector<DataContainer>{{400., 500.}};
347 QTest::newRow("purgeScalar4") << createScalarSeries({1., 2., 3., 4., 5.},
346 QTest::newRow("purgeScalar4") << createScalarSeries({1., 2., 3., 4., 5.},
348 {100., 200., 300., 400., 500.})
347 {100., 200., 300., 400., 500.})
349 << 0. << 7. << QVector<double>{1., 2., 3., 4., 5.}
348 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
350 << QVector<QVector<double> >{{100., 200., 300., 400., 500.}};
349 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
351 QTest::newRow("purgeScalar5") << createScalarSeries({1., 2., 3., 4., 5.},
350 QTest::newRow("purgeScalar5") << createScalarSeries({1., 2., 3., 4., 5.},
352 {100., 200., 300., 400., 500.})
351 {100., 200., 300., 400., 500.})
353 << 5.5 << 7. << QVector<double>{}
352 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
354 << QVector<QVector<double> >{{}};
355 }
353 }
356
354
357 void TestDataSeries::testPurgeScalar()
355 void TestDataSeries::testPurgeScalar()
358 {
356 {
359 testPurge<ScalarSeries>();
357 testPurge<ScalarSeries>();
360 }
358 }
361
359
362 void TestDataSeries::testPurgeVector_data()
360 void TestDataSeries::testPurgeVector_data()
363 {
361 {
364 testPurgeStructure<VectorSeries>();
362 testPurgeStructure<VectorSeries>();
365
363
366 // ////////// //
364 // ////////// //
367 // Test cases //
365 // Test cases //
368 // ////////// //
366 // ////////// //
369
367
370 QTest::newRow("purgeVector") << createVectorSeries({1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.},
368 QTest::newRow("purgeVector") << createVectorSeries({1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.},
371 {11., 12., 13., 14., 15.},
369 {11., 12., 13., 14., 15.},
372 {16., 17., 18., 19., 20.})
370 {16., 17., 18., 19., 20.})
373 << 2. << 4. << QVector<double>{2., 3., 4.}
371 << 2. << 4. << DataContainer{2., 3., 4.}
374 << QVector<QVector<double> >{
372 << std::vector<DataContainer>{
375 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
373 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
376 }
374 }
377
375
378 void TestDataSeries::testPurgeVector()
376 void TestDataSeries::testPurgeVector()
379 {
377 {
380 testPurge<VectorSeries>();
378 testPurge<VectorSeries>();
381 }
379 }
382
380
383 void TestDataSeries::testMinXAxisData_data()
381 void TestDataSeries::testMinXAxisData_data()
384 {
382 {
385 // ////////////// //
383 // ////////////// //
386 // Test structure //
384 // Test structure //
387 // ////////////// //
385 // ////////////// //
388
386
389 // Data series to get min data
387 // Data series to get min data
390 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
388 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
391
389
392 // Min data
390 // Min data
393 QTest::addColumn<double>("min");
391 QTest::addColumn<double>("min");
394
392
395 // Expected results
393 // Expected results
396 QTest::addColumn<bool>(
394 QTest::addColumn<bool>(
397 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
395 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
398 QTest::addColumn<double>(
396 QTest::addColumn<double>(
399 "expectedMin"); // Expected value when method doesn't return end iterator
397 "expectedMin"); // Expected value when method doesn't return end iterator
400
398
401 // ////////// //
399 // ////////// //
402 // Test cases //
400 // Test cases //
403 // ////////// //
401 // ////////// //
404
402
405 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
403 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
406 {100., 200., 300., 400., 500.})
404 {100., 200., 300., 400., 500.})
407 << 0. << true << 1.;
405 << 0. << true << 1.;
408 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
406 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
409 {100., 200., 300., 400., 500.})
407 {100., 200., 300., 400., 500.})
410 << 1. << true << 1.;
408 << 1. << true << 1.;
411 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
409 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
412 {100., 200., 300., 400., 500.})
410 {100., 200., 300., 400., 500.})
413 << 1.1 << true << 2.;
411 << 1.1 << true << 2.;
414 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
412 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
415 {100., 200., 300., 400., 500.})
413 {100., 200., 300., 400., 500.})
416 << 5. << true << 5.;
414 << 5. << true << 5.;
417 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
415 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
418 {100., 200., 300., 400., 500.})
416 {100., 200., 300., 400., 500.})
419 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
417 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
420 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
418 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
421 << std::numeric_limits<double>::quiet_NaN();
419 << std::numeric_limits<double>::quiet_NaN();
422 }
420 }
423
421
424 void TestDataSeries::testMinXAxisData()
422 void TestDataSeries::testMinXAxisData()
425 {
423 {
426 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
424 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
427 QFETCH(double, min);
425 QFETCH(double, min);
428
426
429 QFETCH(bool, expectedOK);
427 QFETCH(bool, expectedOK);
430 QFETCH(double, expectedMin);
428 QFETCH(double, expectedMin);
431
429
432 auto it = dataSeries->minXAxisData(min);
430 auto it = dataSeries->minXAxisData(min);
433
431
434 QCOMPARE(expectedOK, it != dataSeries->cend());
432 QCOMPARE(expectedOK, it != dataSeries->cend());
435
433
436 // If the method doesn't return a end iterator, checks with expected value
434 // If the method doesn't return a end iterator, checks with expected value
437 if (expectedOK) {
435 if (expectedOK) {
438 QCOMPARE(expectedMin, it->x());
436 QCOMPARE(expectedMin, it->x());
439 }
437 }
440 }
438 }
441
439
442 void TestDataSeries::testMaxXAxisData_data()
440 void TestDataSeries::testMaxXAxisData_data()
443 {
441 {
444 // ////////////// //
442 // ////////////// //
445 // Test structure //
443 // Test structure //
446 // ////////////// //
444 // ////////////// //
447
445
448 // Data series to get max data
446 // Data series to get max data
449 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
447 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
450
448
451 // Max data
449 // Max data
452 QTest::addColumn<double>("max");
450 QTest::addColumn<double>("max");
453
451
454 // Expected results
452 // Expected results
455 QTest::addColumn<bool>(
453 QTest::addColumn<bool>(
456 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
454 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
457 QTest::addColumn<double>(
455 QTest::addColumn<double>(
458 "expectedMax"); // Expected value when method doesn't return end iterator
456 "expectedMax"); // Expected value when method doesn't return end iterator
459
457
460 // ////////// //
458 // ////////// //
461 // Test cases //
459 // Test cases //
462 // ////////// //
460 // ////////// //
463
461
464 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
462 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
465 {100., 200., 300., 400., 500.})
463 {100., 200., 300., 400., 500.})
466 << 6. << true << 5.;
464 << 6. << true << 5.;
467 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
465 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
468 {100., 200., 300., 400., 500.})
466 {100., 200., 300., 400., 500.})
469 << 5. << true << 5.;
467 << 5. << true << 5.;
470 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
468 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
471 {100., 200., 300., 400., 500.})
469 {100., 200., 300., 400., 500.})
472 << 4.9 << true << 4.;
470 << 4.9 << true << 4.;
473 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
471 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
474 {100., 200., 300., 400., 500.})
472 {100., 200., 300., 400., 500.})
475 << 1.1 << true << 1.;
473 << 1.1 << true << 1.;
476 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
474 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
477 {100., 200., 300., 400., 500.})
475 {100., 200., 300., 400., 500.})
478 << 1. << true << 1.;
476 << 1. << true << 1.;
479 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
477 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
480 << std::numeric_limits<double>::quiet_NaN();
478 << std::numeric_limits<double>::quiet_NaN();
481 }
479 }
482
480
483 void TestDataSeries::testMaxXAxisData()
481 void TestDataSeries::testMaxXAxisData()
484 {
482 {
485 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
483 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
486 QFETCH(double, max);
484 QFETCH(double, max);
487
485
488 QFETCH(bool, expectedOK);
486 QFETCH(bool, expectedOK);
489 QFETCH(double, expectedMax);
487 QFETCH(double, expectedMax);
490
488
491 auto it = dataSeries->maxXAxisData(max);
489 auto it = dataSeries->maxXAxisData(max);
492
490
493 QCOMPARE(expectedOK, it != dataSeries->cend());
491 QCOMPARE(expectedOK, it != dataSeries->cend());
494
492
495 // If the method doesn't return a end iterator, checks with expected value
493 // If the method doesn't return a end iterator, checks with expected value
496 if (expectedOK) {
494 if (expectedOK) {
497 QCOMPARE(expectedMax, it->x());
495 QCOMPARE(expectedMax, it->x());
498 }
496 }
499 }
497 }
500
498
501 void TestDataSeries::testXAxisRange_data()
499 void TestDataSeries::testXAxisRange_data()
502 {
500 {
503 // ////////////// //
501 // ////////////// //
504 // Test structure //
502 // Test structure //
505 // ////////////// //
503 // ////////////// //
506
504
507 // Data series to get x-axis range
505 // Data series to get x-axis range
508 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
506 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
509
507
510 // Min/max values
508 // Min/max values
511 QTest::addColumn<double>("min");
509 QTest::addColumn<double>("min");
512 QTest::addColumn<double>("max");
510 QTest::addColumn<double>("max");
513
511
514 // Expected values
512 // Expected values
515 QTest::addColumn<QVector<double> >("expectedXAxisData");
513 QTest::addColumn<DataContainer>("expectedXAxisData");
516 QTest::addColumn<QVector<double> >("expectedValuesData");
514 QTest::addColumn<DataContainer>("expectedValuesData");
517
515
518 // ////////// //
516 // ////////// //
519 // Test cases //
517 // Test cases //
520 // ////////// //
518 // ////////// //
521
519
522 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
520 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
523 {100., 200., 300., 400., 500.})
521 {100., 200., 300., 400., 500.})
524 << -1. << 3.2 << QVector<double>{1., 2., 3.}
522 << -1. << 3.2 << DataContainer{1., 2., 3.}
525 << QVector<double>{100., 200., 300.};
523 << DataContainer{100., 200., 300.};
526 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
524 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
527 {100., 200., 300., 400., 500.})
525 {100., 200., 300., 400., 500.})
528 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
526 << 1. << 4. << DataContainer{1., 2., 3., 4.}
529 << QVector<double>{100., 200., 300., 400.};
527 << DataContainer{100., 200., 300., 400.};
530 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
528 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
531 {100., 200., 300., 400., 500.})
529 {100., 200., 300., 400., 500.})
532 << 1. << 3.9 << QVector<double>{1., 2., 3.}
530 << 1. << 3.9 << DataContainer{1., 2., 3.}
533 << QVector<double>{100., 200., 300.};
531 << DataContainer{100., 200., 300.};
534 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
532 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
535 {100., 200., 300., 400., 500.})
533 {100., 200., 300., 400., 500.})
536 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
534 << 0. << 0.9 << DataContainer{} << DataContainer{};
537 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
535 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
538 {100., 200., 300., 400., 500.})
536 {100., 200., 300., 400., 500.})
539 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
537 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
540 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
538 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
541 {100., 200., 300., 400., 500.})
539 {100., 200., 300., 400., 500.})
542 << 2.1 << 6. << QVector<double>{3., 4., 5.}
540 << 2.1 << 6. << DataContainer{3., 4., 5.}
543 << QVector<double>{300., 400., 500.};
541 << DataContainer{300., 400., 500.};
544 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
542 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
545 {100., 200., 300., 400., 500.})
543 {100., 200., 300., 400., 500.})
546 << 6. << 9. << QVector<double>{} << QVector<double>{};
544 << 6. << 9. << DataContainer{} << DataContainer{};
547 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
545 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
548 {100., 200., 300., 400., 500.})
546 {100., 200., 300., 400., 500.})
549 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
547 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
550 }
548 }
551
549
552 void TestDataSeries::testXAxisRange()
550 void TestDataSeries::testXAxisRange()
553 {
551 {
554 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
552 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
555 QFETCH(double, min);
553 QFETCH(double, min);
556 QFETCH(double, max);
554 QFETCH(double, max);
557
555
558 QFETCH(QVector<double>, expectedXAxisData);
556 QFETCH(DataContainer, expectedXAxisData);
559 QFETCH(QVector<double>, expectedValuesData);
557 QFETCH(DataContainer, expectedValuesData);
560
558
561 auto bounds = dataSeries->xAxisRange(min, max);
559 auto bounds = dataSeries->xAxisRange(min, max);
562 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
560 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
563 }
561 }
564
562
565 void TestDataSeries::testValuesBoundsScalar_data()
563 void TestDataSeries::testValuesBoundsScalar_data()
566 {
564 {
567 testValuesBoundsStructure<ScalarSeries>();
565 testValuesBoundsStructure<ScalarSeries>();
568
566
569 // ////////// //
567 // ////////// //
570 // Test cases //
568 // Test cases //
571 // ////////// //
569 // ////////// //
572 auto nan = std::numeric_limits<double>::quiet_NaN();
570 auto nan = std::numeric_limits<double>::quiet_NaN();
573
571
574 QTest::newRow("scalarBounds1")
572 QTest::newRow("scalarBounds1")
575 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
573 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
576 << true << 100. << 500.;
574 << true << 100. << 500.;
577 QTest::newRow("scalarBounds2")
575 QTest::newRow("scalarBounds2")
578 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
576 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
579 << true << 200. << 400.;
577 << true << 200. << 400.;
580 QTest::newRow("scalarBounds3")
578 QTest::newRow("scalarBounds3")
581 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
579 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
582 << false << nan << nan;
580 << false << nan << nan;
583 QTest::newRow("scalarBounds4")
581 QTest::newRow("scalarBounds4")
584 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
582 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
585 << false << nan << nan;
583 << false << nan << nan;
586 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
584 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
587 << 100.;
585 << 100.;
588 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
586 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
589
587
590 // Tests with NaN values: NaN values are not included in min/max search
588 // Tests with NaN values: NaN values are not included in min/max search
591 QTest::newRow("scalarBounds7")
589 QTest::newRow("scalarBounds7")
592 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
590 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
593 << true << 200. << 400.;
591 << true << 200. << 400.;
594 QTest::newRow("scalarBounds8")
592 QTest::newRow("scalarBounds8")
595 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
593 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
596 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
594 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
597 }
595 }
598
596
599 void TestDataSeries::testValuesBoundsScalar()
597 void TestDataSeries::testValuesBoundsScalar()
600 {
598 {
601 testValuesBounds<ScalarSeries>();
599 testValuesBounds<ScalarSeries>();
602 }
600 }
603
601
604 void TestDataSeries::testValuesBoundsVector_data()
602 void TestDataSeries::testValuesBoundsVector_data()
605 {
603 {
606 testValuesBoundsStructure<VectorSeries>();
604 testValuesBoundsStructure<VectorSeries>();
607
605
608 // ////////// //
606 // ////////// //
609 // Test cases //
607 // Test cases //
610 // ////////// //
608 // ////////// //
611 auto nan = std::numeric_limits<double>::quiet_NaN();
609 auto nan = std::numeric_limits<double>::quiet_NaN();
612
610
613 QTest::newRow("vectorBounds1")
611 QTest::newRow("vectorBounds1")
614 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
612 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
615 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
613 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
616 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
614 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
617 QTest::newRow("vectorBounds2")
615 QTest::newRow("vectorBounds2")
618 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
616 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
619 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
617 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
620 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
618 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
621 QTest::newRow("vectorBounds3")
619 QTest::newRow("vectorBounds3")
622 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
620 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
623 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
621 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
624 << 2. << 3. << true << 10. << 24.;
622 << 2. << 3. << true << 10. << 24.;
625
623
626 // Tests with NaN values: NaN values are not included in min/max search
624 // Tests with NaN values: NaN values are not included in min/max search
627 QTest::newRow("vectorBounds4")
625 QTest::newRow("vectorBounds4")
628 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
626 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
629 << nan << nan;
627 << nan << nan;
630 }
628 }
631
629
632 void TestDataSeries::testValuesBoundsVector()
630 void TestDataSeries::testValuesBoundsVector()
633 {
631 {
634 testValuesBounds<VectorSeries>();
632 testValuesBounds<VectorSeries>();
635 }
633 }
636
634
637 QTEST_MAIN(TestDataSeries)
635 QTEST_MAIN(TestDataSeries)
638 #include "TestDataSeries.moc"
636 #include "TestDataSeries.moc"
@@ -1,181 +1,181
1 #include "Data/ArrayData.h"
1 #include "Data/ArrayData.h"
2 #include <QObject>
2 #include <QObject>
3 #include <QtTest>
3 #include <QtTest>
4
4
5 namespace {
5 namespace {
6
6
7 void verifyArrayData(const ArrayData<1> &arrayData, const QVector<double> &expectedData)
7 using DataContainer = std::vector<double>;
8
9 void verifyArrayData(const ArrayData<1> &arrayData, const DataContainer &expectedData)
8 {
10 {
9 QVERIFY(std::equal(
11 QVERIFY(std::equal(
10 arrayData.cbegin(), arrayData.cend(), expectedData.cbegin(), expectedData.cend(),
12 arrayData.cbegin(), arrayData.cend(), expectedData.cbegin(), expectedData.cend(),
11 [](const auto &it, const auto &expectedData) { return it.at(0) == expectedData; }));
13 [](const auto &it, const auto &expectedData) { return it.at(0) == expectedData; }));
12 }
14 }
13
15
14 } // namespace
16 } // namespace
15
17
16 class TestOneDimArrayData : public QObject {
18 class TestOneDimArrayData : public QObject {
17 Q_OBJECT
19 Q_OBJECT
18 private slots:
20 private slots:
19 /// Tests @sa ArrayData::data()
21 /// Tests @sa ArrayData::data()
20 void testData_data();
22 void testData_data();
21 void testData();
23 void testData();
22
24
23 /// Tests @sa ArrayData::add()
25 /// Tests @sa ArrayData::add()
24 void testAdd_data();
26 void testAdd_data();
25 void testAdd();
27 void testAdd();
26
28
27 /// Tests @sa ArrayData::at(int index)
29 /// Tests @sa ArrayData::at(int index)
28 void testAt_data();
30 void testAt_data();
29 void testAt();
31 void testAt();
30
32
31 /// Tests @sa ArrayData::clear()
33 /// Tests @sa ArrayData::clear()
32 void testClear_data();
34 void testClear_data();
33 void testClear();
35 void testClear();
34
36
35 /// Tests @sa ArrayData::size()
37 /// Tests @sa ArrayData::size()
36 void testSize_data();
38 void testSize_data();
37 void testSize();
39 void testSize();
38
40
39 /// Tests @sa ArrayData::sort()
41 /// Tests @sa ArrayData::sort()
40 void testSort_data();
42 void testSort_data();
41 void testSort();
43 void testSort();
42 };
44 };
43
45
44 void TestOneDimArrayData::testData_data()
46 void TestOneDimArrayData::testData_data()
45 {
47 {
46 // Test structure
48 // Test structure
47 QTest::addColumn<QVector<double> >("inputData"); // array's data input
49 QTest::addColumn<DataContainer>("inputData"); // array's data input
48 QTest::addColumn<QVector<double> >("expectedData"); // expected data
50 QTest::addColumn<DataContainer>("expectedData"); // expected data
49
51
50 // Test cases
52 // Test cases
51 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.}
53 QTest::newRow("data1") << DataContainer{1., 2., 3., 4., 5.}
52 << QVector<double>{1., 2., 3., 4., 5.};
54 << DataContainer{1., 2., 3., 4., 5.};
53 }
55 }
54
56
55 void TestOneDimArrayData::testData()
57 void TestOneDimArrayData::testData()
56 {
58 {
57 QFETCH(QVector<double>, inputData);
59 QFETCH(DataContainer, inputData);
58 QFETCH(QVector<double>, expectedData);
60 QFETCH(DataContainer, expectedData);
59
61
60 ArrayData<1> arrayData{inputData};
62 ArrayData<1> arrayData{inputData};
61 verifyArrayData(arrayData, expectedData);
63 verifyArrayData(arrayData, expectedData);
62 }
64 }
63
65
64 void TestOneDimArrayData::testAdd_data()
66 void TestOneDimArrayData::testAdd_data()
65 {
67 {
66 // Test structure
68 // Test structure
67 QTest::addColumn<QVector<double> >("inputData"); // array's data input
69 QTest::addColumn<DataContainer>("inputData"); // array's data input
68 QTest::addColumn<QVector<double> >("otherData"); // array data's input to merge with
70 QTest::addColumn<DataContainer>("otherData"); // array data's input to merge with
69 QTest::addColumn<bool>("prepend"); // prepend or append merge
71 QTest::addColumn<bool>("prepend"); // prepend or append merge
70 QTest::addColumn<QVector<double> >("expectedData"); // expected data after merge
72 QTest::addColumn<DataContainer>("expectedData"); // expected data after merge
71
73
72 // Test cases
74 // Test cases
73 QTest::newRow("appendMerge") << QVector<double>{1., 2., 3., 4., 5.}
75 QTest::newRow("appendMerge") << DataContainer{1., 2., 3., 4., 5.} << DataContainer{6., 7., 8.}
74 << QVector<double>{6., 7., 8.} << false
76 << false << DataContainer{1., 2., 3., 4., 5., 6., 7., 8.};
75 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8.};
77 QTest::newRow("prependMerge") << DataContainer{1., 2., 3., 4., 5.} << DataContainer{6., 7., 8.}
76 QTest::newRow("prependMerge") << QVector<double>{1., 2., 3., 4., 5.}
78 << true << DataContainer{6., 7., 8., 1., 2., 3., 4., 5.};
77 << QVector<double>{6., 7., 8.} << true
78 << QVector<double>{6., 7., 8., 1., 2., 3., 4., 5.};
79 }
79 }
80
80
81 void TestOneDimArrayData::testAdd()
81 void TestOneDimArrayData::testAdd()
82 {
82 {
83 QFETCH(QVector<double>, inputData);
83 QFETCH(DataContainer, inputData);
84 QFETCH(QVector<double>, otherData);
84 QFETCH(DataContainer, otherData);
85 QFETCH(bool, prepend);
85 QFETCH(bool, prepend);
86 QFETCH(QVector<double>, expectedData);
86 QFETCH(DataContainer, expectedData);
87
87
88 ArrayData<1> arrayData{inputData};
88 ArrayData<1> arrayData{inputData};
89 ArrayData<1> other{otherData};
89 ArrayData<1> other{otherData};
90
90
91 arrayData.add(other, prepend);
91 arrayData.add(other, prepend);
92 verifyArrayData(arrayData, expectedData);
92 verifyArrayData(arrayData, expectedData);
93 }
93 }
94
94
95 void TestOneDimArrayData::testAt_data()
95 void TestOneDimArrayData::testAt_data()
96 {
96 {
97 // Test structure
97 // Test structure
98 QTest::addColumn<QVector<double> >("inputData"); // array data's input
98 QTest::addColumn<DataContainer>("inputData"); // array data's input
99 QTest::addColumn<int>("index"); // index to retrieve data
99 QTest::addColumn<int>("index"); // index to retrieve data
100 QTest::addColumn<double>("expectedData"); // expected data at index
100 QTest::addColumn<double>("expectedData"); // expected data at index
101
101
102 // Test cases
102 // Test cases
103 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << 0 << 1.;
103 QTest::newRow("data1") << DataContainer{1., 2., 3., 4., 5.} << 0 << 1.;
104 QTest::newRow("data2") << QVector<double>{1., 2., 3., 4., 5.} << 3 << 4.;
104 QTest::newRow("data2") << DataContainer{1., 2., 3., 4., 5.} << 3 << 4.;
105 }
105 }
106
106
107 void TestOneDimArrayData::testAt()
107 void TestOneDimArrayData::testAt()
108 {
108 {
109 QFETCH(QVector<double>, inputData);
109 QFETCH(DataContainer, inputData);
110 QFETCH(int, index);
110 QFETCH(int, index);
111 QFETCH(double, expectedData);
111 QFETCH(double, expectedData);
112
112
113 ArrayData<1> arrayData{inputData};
113 ArrayData<1> arrayData{inputData};
114 QVERIFY(arrayData.at(index) == expectedData);
114 QVERIFY(arrayData.at(index) == expectedData);
115 }
115 }
116
116
117 void TestOneDimArrayData::testClear_data()
117 void TestOneDimArrayData::testClear_data()
118 {
118 {
119 // Test structure
119 // Test structure
120 QTest::addColumn<QVector<double> >("inputData"); // array data's input
120 QTest::addColumn<DataContainer>("inputData"); // array data's input
121
121
122 // Test cases
122 // Test cases
123 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.};
123 QTest::newRow("data1") << DataContainer{1., 2., 3., 4., 5.};
124 }
124 }
125
125
126 void TestOneDimArrayData::testClear()
126 void TestOneDimArrayData::testClear()
127 {
127 {
128 QFETCH(QVector<double>, inputData);
128 QFETCH(DataContainer, inputData);
129
129
130 ArrayData<1> arrayData{inputData};
130 ArrayData<1> arrayData{inputData};
131 arrayData.clear();
131 arrayData.clear();
132 verifyArrayData(arrayData, QVector<double>{});
132 verifyArrayData(arrayData, DataContainer{});
133 }
133 }
134
134
135 void TestOneDimArrayData::testSize_data()
135 void TestOneDimArrayData::testSize_data()
136 {
136 {
137 // Test structure
137 // Test structure
138 QTest::addColumn<QVector<double> >("inputData"); // array data's input
138 QTest::addColumn<DataContainer>("inputData"); // array data's input
139 QTest::addColumn<int>("expectedSize"); // expected array data size
139 QTest::addColumn<int>("expectedSize"); // expected array data size
140
140
141 // Test cases
141 // Test cases
142 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << 5;
142 QTest::newRow("data1") << DataContainer{1., 2., 3., 4., 5.} << 5;
143 }
143 }
144
144
145 void TestOneDimArrayData::testSize()
145 void TestOneDimArrayData::testSize()
146 {
146 {
147 QFETCH(QVector<double>, inputData);
147 QFETCH(DataContainer, inputData);
148 QFETCH(int, expectedSize);
148 QFETCH(int, expectedSize);
149
149
150 ArrayData<1> arrayData{inputData};
150 ArrayData<1> arrayData{inputData};
151 QVERIFY(arrayData.size() == expectedSize);
151 QVERIFY(arrayData.size() == expectedSize);
152 }
152 }
153
153
154 void TestOneDimArrayData::testSort_data()
154 void TestOneDimArrayData::testSort_data()
155 {
155 {
156 // Test structure
156 // Test structure
157 QTest::addColumn<QVector<double> >("inputData"); // array data's input
157 QTest::addColumn<DataContainer>("inputData"); // array data's input
158 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
158 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
159 QTest::addColumn<QVector<double> >("expectedData"); // expected data after sorting
159 QTest::addColumn<DataContainer>("expectedData"); // expected data after sorting
160
160
161 // Test cases
161 // Test cases
162 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << std::vector<int>{0, 2, 3, 1, 4}
162 QTest::newRow("data1") << DataContainer{1., 2., 3., 4., 5.} << std::vector<int>{0, 2, 3, 1, 4}
163 << QVector<double>{1., 3., 4., 2., 5.};
163 << DataContainer{1., 3., 4., 2., 5.};
164 QTest::newRow("data2") << QVector<double>{1., 2., 3., 4., 5.} << std::vector<int>{4, 1, 2, 3, 0}
164 QTest::newRow("data2") << DataContainer{1., 2., 3., 4., 5.} << std::vector<int>{4, 1, 2, 3, 0}
165 << QVector<double>{5., 2., 3., 4., 1.};
165 << DataContainer{5., 2., 3., 4., 1.};
166 }
166 }
167
167
168 void TestOneDimArrayData::testSort()
168 void TestOneDimArrayData::testSort()
169 {
169 {
170 QFETCH(QVector<double>, inputData);
170 QFETCH(DataContainer, inputData);
171 QFETCH(std::vector<int>, sortPermutation);
171 QFETCH(std::vector<int>, sortPermutation);
172 QFETCH(QVector<double>, expectedData);
172 QFETCH(DataContainer, expectedData);
173
173
174 ArrayData<1> arrayData{inputData};
174 ArrayData<1> arrayData{inputData};
175 auto sortedArrayData = arrayData.sort(sortPermutation);
175 auto sortedArrayData = arrayData.sort(sortPermutation);
176 QVERIFY(sortedArrayData != nullptr);
176 QVERIFY(sortedArrayData != nullptr);
177 verifyArrayData(*sortedArrayData, expectedData);
177 verifyArrayData(*sortedArrayData, expectedData);
178 }
178 }
179
179
180 QTEST_MAIN(TestOneDimArrayData)
180 QTEST_MAIN(TestOneDimArrayData)
181 #include "TestOneDimArrayData.moc"
181 #include "TestOneDimArrayData.moc"
@@ -1,239 +1,240
1 #include "Data/ArrayData.h"
1 #include "Data/ArrayData.h"
2 #include <QObject>
2 #include <QObject>
3 #include <QtTest>
3 #include <QtTest>
4
4
5 using Container = QVector<QVector<double> >;
5 using DataContainer = std::vector<double>;
6 using InputData = QPair<QVector<double>, int>;
6 using Container = std::vector<DataContainer>;
7 using InputData = QPair<DataContainer, int>;
7
8
8 namespace {
9 namespace {
9
10
10 InputData flatten(const Container &container)
11 InputData flatten(const Container &container)
11 {
12 {
12 if (container.isEmpty()) {
13 if (container.empty()) {
13 return {};
14 return {};
14 }
15 }
15
16
16 // We assume here that each component of the container have the same size
17 // We assume here that each component of the container have the same size
17 auto containerSize = container.size();
18 auto containerSize = container.size();
18 auto componentSize = container.first().size();
19 auto componentSize = container.front().size();
19
20
20 auto result = QVector<double>{};
21 auto result = DataContainer{};
21 result.reserve(componentSize * containerSize);
22 result.reserve(componentSize * containerSize);
22
23
23 for (auto i = 0; i < componentSize; ++i) {
24 for (auto i = 0; i < componentSize; ++i) {
24 for (auto j = 0; j < containerSize; ++j) {
25 for (auto j = 0; j < containerSize; ++j) {
25 result.append(container.at(j).at(i));
26 result.push_back(container.at(j).at(i));
26 }
27 }
27 }
28 }
28
29
29 return {result, containerSize};
30 return {result, static_cast<int>(containerSize)};
30 }
31 }
31
32
32 void verifyArrayData(const ArrayData<2> &arrayData, const Container &expectedData)
33 void verifyArrayData(const ArrayData<2> &arrayData, const Container &expectedData)
33 {
34 {
34 auto verifyComponent = [&arrayData](const auto &componentData, const auto &equalFun) {
35 auto verifyComponent = [&arrayData](const auto &componentData, const auto &equalFun) {
35 QVERIFY(std::equal(arrayData.cbegin(), arrayData.cend(), componentData.cbegin(),
36 QVERIFY(std::equal(arrayData.cbegin(), arrayData.cend(), componentData.cbegin(),
36 componentData.cend(),
37 componentData.cend(),
37 [&equalFun](const auto &dataSeriesIt, const auto &expectedValue) {
38 [&equalFun](const auto &dataSeriesIt, const auto &expectedValue) {
38 return equalFun(dataSeriesIt, expectedValue);
39 return equalFun(dataSeriesIt, expectedValue);
39 }));
40 }));
40 };
41 };
41
42
42 for (auto i = 0; i < expectedData.size(); ++i) {
43 for (auto i = 0; i < expectedData.size(); ++i) {
43 verifyComponent(expectedData.at(i), [i](const auto &seriesIt, const auto &value) {
44 verifyComponent(expectedData.at(i), [i](const auto &seriesIt, const auto &value) {
44 return seriesIt.at(i) == value;
45 return seriesIt.at(i) == value;
45 });
46 });
46 }
47 }
47 }
48 }
48
49
49 } // namespace
50 } // namespace
50
51
51 class TestTwoDimArrayData : public QObject {
52 class TestTwoDimArrayData : public QObject {
52 Q_OBJECT
53 Q_OBJECT
53 private slots:
54 private slots:
54 /// Tests @sa ArrayData ctor
55 /// Tests @sa ArrayData ctor
55 void testCtor_data();
56 void testCtor_data();
56 void testCtor();
57 void testCtor();
57
58
58 /// Tests @sa ArrayData::add()
59 /// Tests @sa ArrayData::add()
59 void testAdd_data();
60 void testAdd_data();
60 void testAdd();
61 void testAdd();
61
62
62 /// Tests @sa ArrayData::clear()
63 /// Tests @sa ArrayData::clear()
63 void testClear_data();
64 void testClear_data();
64 void testClear();
65 void testClear();
65
66
66 /// Tests @sa ArrayData::size()
67 /// Tests @sa ArrayData::size()
67 void testSize_data();
68 void testSize_data();
68 void testSize();
69 void testSize();
69
70
70 /// Tests @sa ArrayData::sort()
71 /// Tests @sa ArrayData::sort()
71 void testSort_data();
72 void testSort_data();
72 void testSort();
73 void testSort();
73 };
74 };
74
75
75 void TestTwoDimArrayData::testCtor_data()
76 void TestTwoDimArrayData::testCtor_data()
76 {
77 {
77 // Test structure
78 // Test structure
78 QTest::addColumn<InputData>("inputData"); // array data's input
79 QTest::addColumn<InputData>("inputData"); // array data's input
79 QTest::addColumn<bool>("success"); // array data has been successfully constructed
80 QTest::addColumn<bool>("success"); // array data has been successfully constructed
80 QTest::addColumn<Container>("expectedData"); // expected array data (when success)
81 QTest::addColumn<Container>("expectedData"); // expected array data (when success)
81
82
82 // Test cases
83 // Test cases
83 QTest::newRow("validInput") << flatten(Container{{1., 2., 3., 4., 5.},
84 QTest::newRow("validInput") << flatten(Container{{1., 2., 3., 4., 5.},
84 {6., 7., 8., 9., 10.},
85 {6., 7., 8., 9., 10.},
85 {11., 12., 13., 14., 15.}})
86 {11., 12., 13., 14., 15.}})
86 << true << Container{{1., 2., 3., 4., 5.},
87 << true << Container{{1., 2., 3., 4., 5.},
87 {6., 7., 8., 9., 10.},
88 {6., 7., 8., 9., 10.},
88 {11., 12., 13., 14., 15.}};
89 {11., 12., 13., 14., 15.}};
89 QTest::newRow("invalidInput (invalid data size")
90 QTest::newRow("invalidInput (invalid data size")
90 << InputData{{1., 2., 3., 4., 5., 6., 7.}, 3} << false << Container{{}, {}, {}};
91 << InputData{{1., 2., 3., 4., 5., 6., 7.}, 3} << false << Container{{}, {}, {}};
91 QTest::newRow("invalidInput (less than two components")
92 QTest::newRow("invalidInput (less than two components")
92 << flatten(Container{{1., 2., 3., 4., 5.}}) << false << Container{{}, {}, {}};
93 << flatten(Container{{1., 2., 3., 4., 5.}}) << false << Container{{}, {}, {}};
93 }
94 }
94
95
95 void TestTwoDimArrayData::testCtor()
96 void TestTwoDimArrayData::testCtor()
96 {
97 {
97 QFETCH(InputData, inputData);
98 QFETCH(InputData, inputData);
98 QFETCH(bool, success);
99 QFETCH(bool, success);
99
100
100 if (success) {
101 if (success) {
101 QFETCH(Container, expectedData);
102 QFETCH(Container, expectedData);
102
103
103 ArrayData<2> arrayData{inputData.first, inputData.second};
104 ArrayData<2> arrayData{inputData.first, inputData.second};
104 verifyArrayData(arrayData, expectedData);
105 verifyArrayData(arrayData, expectedData);
105 }
106 }
106 else {
107 else {
107 QVERIFY_EXCEPTION_THROWN(ArrayData<2>(inputData.first, inputData.second),
108 QVERIFY_EXCEPTION_THROWN(ArrayData<2>(inputData.first, inputData.second),
108 std::invalid_argument);
109 std::invalid_argument);
109 }
110 }
110 }
111 }
111
112
112 void TestTwoDimArrayData::testAdd_data()
113 void TestTwoDimArrayData::testAdd_data()
113 {
114 {
114 // Test structure
115 // Test structure
115 QTest::addColumn<InputData>("inputData"); // array's data input
116 QTest::addColumn<InputData>("inputData"); // array's data input
116 QTest::addColumn<InputData>("otherData"); // array data's input to merge with
117 QTest::addColumn<InputData>("otherData"); // array data's input to merge with
117 QTest::addColumn<bool>("prepend"); // prepend or append merge
118 QTest::addColumn<bool>("prepend"); // prepend or append merge
118 QTest::addColumn<Container>("expectedData"); // expected data after merge
119 QTest::addColumn<Container>("expectedData"); // expected data after merge
119
120
120 // Test cases
121 // Test cases
121 auto inputData = flatten(
122 auto inputData = flatten(
122 Container{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}});
123 Container{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}});
123
124
124 auto vectorContainer = flatten(Container{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}});
125 auto vectorContainer = flatten(Container{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}});
125 auto tensorContainer = flatten(Container{{6., 7., 8.},
126 auto tensorContainer = flatten(Container{{6., 7., 8.},
126 {16., 17., 18.},
127 {16., 17., 18.},
127 {26., 27., 28},
128 {26., 27., 28},
128 {36., 37., 38.},
129 {36., 37., 38.},
129 {46., 47., 48.},
130 {46., 47., 48.},
130 {56., 57., 58}});
131 {56., 57., 58}});
131
132
132 QTest::newRow("appendMerge") << inputData << vectorContainer << false
133 QTest::newRow("appendMerge") << inputData << vectorContainer << false
133 << Container{{1., 2., 3., 4., 5., 6., 7., 8.},
134 << Container{{1., 2., 3., 4., 5., 6., 7., 8.},
134 {11., 12., 13., 14., 15., 16., 17., 18.},
135 {11., 12., 13., 14., 15., 16., 17., 18.},
135 {21., 22., 23., 24., 25., 26., 27., 28}};
136 {21., 22., 23., 24., 25., 26., 27., 28}};
136 QTest::newRow("prependMerge") << inputData << vectorContainer << true
137 QTest::newRow("prependMerge") << inputData << vectorContainer << true
137 << Container{{6., 7., 8., 1., 2., 3., 4., 5.},
138 << Container{{6., 7., 8., 1., 2., 3., 4., 5.},
138 {16., 17., 18., 11., 12., 13., 14., 15.},
139 {16., 17., 18., 11., 12., 13., 14., 15.},
139 {26., 27., 28, 21., 22., 23., 24., 25.}};
140 {26., 27., 28, 21., 22., 23., 24., 25.}};
140 QTest::newRow("invalidMerge") << inputData << tensorContainer << false
141 QTest::newRow("invalidMerge") << inputData << tensorContainer << false
141 << Container{{1., 2., 3., 4., 5.},
142 << Container{{1., 2., 3., 4., 5.},
142 {11., 12., 13., 14., 15.},
143 {11., 12., 13., 14., 15.},
143 {21., 22., 23., 24., 25.}};
144 {21., 22., 23., 24., 25.}};
144 }
145 }
145
146
146 void TestTwoDimArrayData::testAdd()
147 void TestTwoDimArrayData::testAdd()
147 {
148 {
148 QFETCH(InputData, inputData);
149 QFETCH(InputData, inputData);
149 QFETCH(InputData, otherData);
150 QFETCH(InputData, otherData);
150 QFETCH(bool, prepend);
151 QFETCH(bool, prepend);
151 QFETCH(Container, expectedData);
152 QFETCH(Container, expectedData);
152
153
153 ArrayData<2> arrayData{inputData.first, inputData.second};
154 ArrayData<2> arrayData{inputData.first, inputData.second};
154 ArrayData<2> other{otherData.first, otherData.second};
155 ArrayData<2> other{otherData.first, otherData.second};
155
156
156 arrayData.add(other, prepend);
157 arrayData.add(other, prepend);
157
158
158 verifyArrayData(arrayData, expectedData);
159 verifyArrayData(arrayData, expectedData);
159 }
160 }
160
161
161 void TestTwoDimArrayData::testClear_data()
162 void TestTwoDimArrayData::testClear_data()
162 {
163 {
163 // Test structure
164 // Test structure
164 QTest::addColumn<InputData>("inputData"); // array data's input
165 QTest::addColumn<InputData>("inputData"); // array data's input
165
166
166 // Test cases
167 // Test cases
167 QTest::newRow("data1") << flatten(
168 QTest::newRow("data1") << flatten(
168 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}});
169 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}});
169 }
170 }
170
171
171 void TestTwoDimArrayData::testClear()
172 void TestTwoDimArrayData::testClear()
172 {
173 {
173 QFETCH(InputData, inputData);
174 QFETCH(InputData, inputData);
174
175
175 ArrayData<2> arrayData{inputData.first, inputData.second};
176 ArrayData<2> arrayData{inputData.first, inputData.second};
176 arrayData.clear();
177 arrayData.clear();
177
178
178 auto emptyData = Container(inputData.second, QVector<double>{});
179 auto emptyData = Container(inputData.second, DataContainer{});
179 verifyArrayData(arrayData, emptyData);
180 verifyArrayData(arrayData, emptyData);
180 }
181 }
181
182
182 void TestTwoDimArrayData::testSize_data()
183 void TestTwoDimArrayData::testSize_data()
183 {
184 {
184 // Test structure
185 // Test structure
185 QTest::addColumn<InputData>("inputData"); // array data's input
186 QTest::addColumn<InputData>("inputData"); // array data's input
186 QTest::addColumn<int>("expectedSize"); // expected array data size
187 QTest::addColumn<int>("expectedSize"); // expected array data size
187
188
188 // Test cases
189 // Test cases
189 QTest::newRow("data1") << flatten(Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}}) << 5;
190 QTest::newRow("data1") << flatten(Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}}) << 5;
190 QTest::newRow("data2") << flatten(Container{{1., 2., 3., 4., 5.},
191 QTest::newRow("data2") << flatten(Container{{1., 2., 3., 4., 5.},
191 {6., 7., 8., 9., 10.},
192 {6., 7., 8., 9., 10.},
192 {11., 12., 13., 14., 15.}})
193 {11., 12., 13., 14., 15.}})
193 << 5;
194 << 5;
194 }
195 }
195
196
196 void TestTwoDimArrayData::testSize()
197 void TestTwoDimArrayData::testSize()
197 {
198 {
198 QFETCH(InputData, inputData);
199 QFETCH(InputData, inputData);
199 QFETCH(int, expectedSize);
200 QFETCH(int, expectedSize);
200
201
201 ArrayData<2> arrayData{inputData.first, inputData.second};
202 ArrayData<2> arrayData{inputData.first, inputData.second};
202 QVERIFY(arrayData.size() == expectedSize);
203 QVERIFY(arrayData.size() == expectedSize);
203 }
204 }
204
205
205 void TestTwoDimArrayData::testSort_data()
206 void TestTwoDimArrayData::testSort_data()
206 {
207 {
207 // Test structure
208 // Test structure
208 QTest::addColumn<InputData>("inputData"); // array data's input
209 QTest::addColumn<InputData>("inputData"); // array data's input
209 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
210 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
210 QTest::addColumn<Container>("expectedData"); // expected data after sorting
211 QTest::addColumn<Container>("expectedData"); // expected data after sorting
211
212
212 // Test cases
213 // Test cases
213 QTest::newRow("data1")
214 QTest::newRow("data1")
214 << flatten(
215 << flatten(
215 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
216 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
216 << std::vector<int>{0, 2, 3, 1, 4}
217 << std::vector<int>{0, 2, 3, 1, 4}
217 << Container{{1., 3., 4., 2., 5.}, {6., 8., 9., 7., 10.}, {11., 13., 14., 12., 15.}};
218 << Container{{1., 3., 4., 2., 5.}, {6., 8., 9., 7., 10.}, {11., 13., 14., 12., 15.}};
218 QTest::newRow("data2")
219 QTest::newRow("data2")
219 << flatten(
220 << flatten(
220 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
221 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
221 << std::vector<int>{2, 4, 3, 0, 1}
222 << std::vector<int>{2, 4, 3, 0, 1}
222 << Container{{3., 5., 4., 1., 2.}, {8., 10., 9., 6., 7.}, {13., 15., 14., 11., 12.}};
223 << Container{{3., 5., 4., 1., 2.}, {8., 10., 9., 6., 7.}, {13., 15., 14., 11., 12.}};
223 }
224 }
224
225
225 void TestTwoDimArrayData::testSort()
226 void TestTwoDimArrayData::testSort()
226 {
227 {
227 QFETCH(InputData, inputData);
228 QFETCH(InputData, inputData);
228 QFETCH(std::vector<int>, sortPermutation);
229 QFETCH(std::vector<int>, sortPermutation);
229 QFETCH(Container, expectedData);
230 QFETCH(Container, expectedData);
230
231
231 ArrayData<2> arrayData{inputData.first, inputData.second};
232 ArrayData<2> arrayData{inputData.first, inputData.second};
232 auto sortedArrayData = arrayData.sort(sortPermutation);
233 auto sortedArrayData = arrayData.sort(sortPermutation);
233 QVERIFY(sortedArrayData != nullptr);
234 QVERIFY(sortedArrayData != nullptr);
234
235
235 verifyArrayData(*sortedArrayData, expectedData);
236 verifyArrayData(*sortedArrayData, expectedData);
236 }
237 }
237
238
238 QTEST_MAIN(TestTwoDimArrayData)
239 QTEST_MAIN(TestTwoDimArrayData)
239 #include "TestTwoDimArrayData.moc"
240 #include "TestTwoDimArrayData.moc"
@@ -1,216 +1,217
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Common/DateUtils.h>
3 #include <Common/DateUtils.h>
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5 #include <Data/VectorSeries.h>
5 #include <Data/VectorSeries.h>
6
6
7 #include <QDateTime>
7 #include <QDateTime>
8 #include <QFile>
8 #include <QFile>
9 #include <QRegularExpression>
9 #include <QRegularExpression>
10
10
11 #include <cmath>
11 #include <cmath>
12
12
13 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
13 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
14
14
15 namespace {
15 namespace {
16
16
17 /// Message in result file when the file was not found on server
17 /// Message in result file when the file was not found on server
18 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
18 const auto FILE_NOT_FOUND_MESSAGE = QStringLiteral("Not Found");
19
19
20 /// Format for dates in result files
20 /// Format for dates in result files
21 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
21 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
22
22
23 /// Separator between values in a result line
23 /// Separator between values in a result line
24 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
24 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
25
25
26 /// Regex to find unit in a line. Examples of valid lines:
26 /// Regex to find unit in a line. Examples of valid lines:
27 /// ... - Units : nT - ...
27 /// ... - Units : nT - ...
28 /// ... -Units:nT- ...
28 /// ... -Units:nT- ...
29 /// ... -Units: mΒ²- ...
29 /// ... -Units: mΒ²- ...
30 /// ... - Units : m/s - ...
30 /// ... - Units : m/s - ...
31 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
31 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
32
32
33 /// Converts a string date to a double date
33 /// Converts a string date to a double date
34 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
34 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
35 double doubleDate(const QString &stringDate) noexcept
35 double doubleDate(const QString &stringDate) noexcept
36 {
36 {
37 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
37 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
38 dateTime.setTimeSpec(Qt::UTC);
38 dateTime.setTimeSpec(Qt::UTC);
39 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
39 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
40 : std::numeric_limits<double>::quiet_NaN();
40 : std::numeric_limits<double>::quiet_NaN();
41 }
41 }
42
42
43 /// Checks if a line is a comment line
43 /// Checks if a line is a comment line
44 bool isCommentLine(const QString &line)
44 bool isCommentLine(const QString &line)
45 {
45 {
46 return line.startsWith("#");
46 return line.startsWith("#");
47 }
47 }
48
48
49 /// @return the number of lines to be read depending on the type of value passed in parameter
49 /// @return the number of lines to be read depending on the type of value passed in parameter
50 int nbValues(AmdaResultParser::ValueType valueType) noexcept
50 int nbValues(AmdaResultParser::ValueType valueType) noexcept
51 {
51 {
52 switch (valueType) {
52 switch (valueType) {
53 case AmdaResultParser::ValueType::SCALAR:
53 case AmdaResultParser::ValueType::SCALAR:
54 return 1;
54 return 1;
55 case AmdaResultParser::ValueType::VECTOR:
55 case AmdaResultParser::ValueType::VECTOR:
56 return 3;
56 return 3;
57 case AmdaResultParser::ValueType::UNKNOWN:
57 case AmdaResultParser::ValueType::UNKNOWN:
58 // Invalid case
58 // Invalid case
59 break;
59 break;
60 }
60 }
61
61
62 // Invalid cases
62 // Invalid cases
63 qCCritical(LOG_AmdaResultParser())
63 qCCritical(LOG_AmdaResultParser())
64 << QObject::tr("Can't get the number of values to read: unsupported type");
64 << QObject::tr("Can't get the number of values to read: unsupported type");
65 return 0;
65 return 0;
66 }
66 }
67
67
68 /**
68 /**
69 * Reads stream to retrieve x-axis unit
69 * Reads stream to retrieve x-axis unit
70 * @param stream the stream to read
70 * @param stream the stream to read
71 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
71 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
72 * error occured during reading
72 * error occured during reading
73 */
73 */
74 Unit readXAxisUnit(QTextStream &stream)
74 Unit readXAxisUnit(QTextStream &stream)
75 {
75 {
76 QString line{};
76 QString line{};
77
77
78 // Searches unit in the comment lines
78 // Searches unit in the comment lines
79 while (stream.readLineInto(&line) && isCommentLine(line)) {
79 while (stream.readLineInto(&line) && isCommentLine(line)) {
80 auto match = UNIT_REGEX.match(line);
80 auto match = UNIT_REGEX.match(line);
81 if (match.hasMatch()) {
81 if (match.hasMatch()) {
82 return Unit{match.captured(1), true};
82 return Unit{match.captured(1), true};
83 }
83 }
84 }
84 }
85
85
86 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
86 qCWarning(LOG_AmdaResultParser()) << QObject::tr("The unit could not be found in the file");
87
87
88 // Error cases
88 // Error cases
89 return Unit{{}, true};
89 return Unit{{}, true};
90 }
90 }
91
91
92 /**
92 /**
93 * Reads stream to retrieve results
93 * Reads stream to retrieve results
94 * @param stream the stream to read
94 * @param stream the stream to read
95 * @return the pair of vectors x-axis data/values data that has been read in the stream
95 * @return the pair of vectors x-axis data/values data that has been read in the stream
96 */
96 */
97 QPair<QVector<double>, QVector<QVector<double> > >
97 QPair<QVector<double>, QVector<QVector<double> > >
98 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
98 readResults(QTextStream &stream, AmdaResultParser::ValueType valueType)
99 {
99 {
100 auto expectedNbValues = nbValues(valueType);
100 auto expectedNbValues = nbValues(valueType);
101
101
102 auto xData = QVector<double>{};
102 auto xData = QVector<double>{};
103 auto valuesData = QVector<QVector<double> >(expectedNbValues);
103 auto valuesData = QVector<QVector<double> >(expectedNbValues);
104
104
105 QString line{};
105 QString line{};
106
106
107 while (stream.readLineInto(&line)) {
107 while (stream.readLineInto(&line)) {
108 // Ignore comment lines
108 // Ignore comment lines
109 if (!isCommentLine(line)) {
109 if (!isCommentLine(line)) {
110 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
110 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
111 if (lineData.size() == expectedNbValues + 1) {
111 if (lineData.size() == expectedNbValues + 1) {
112 // X : the data is converted from date to double (in secs)
112 // X : the data is converted from date to double (in secs)
113 auto x = doubleDate(lineData.at(0));
113 auto x = doubleDate(lineData.at(0));
114
114
115 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
115 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
116 if (!std::isnan(x)) {
116 if (!std::isnan(x)) {
117 xData.push_back(x);
117 xData.push_back(x);
118
118
119 // Values
119 // Values
120 for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) {
120 for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) {
121 auto column = valueIndex + 1;
121 auto column = valueIndex + 1;
122
122
123 bool valueOk;
123 bool valueOk;
124 auto value = lineData.at(column).toDouble(&valueOk);
124 auto value = lineData.at(column).toDouble(&valueOk);
125
125
126 if (!valueOk) {
126 if (!valueOk) {
127 qCWarning(LOG_AmdaResultParser())
127 qCWarning(LOG_AmdaResultParser())
128 << QObject::tr(
128 << QObject::tr(
129 "Value from (line %1, column %2) is invalid and will be "
129 "Value from (line %1, column %2) is invalid and will be "
130 "converted to NaN")
130 "converted to NaN")
131 .arg(line, column);
131 .arg(line, column);
132 value = std::numeric_limits<double>::quiet_NaN();
132 value = std::numeric_limits<double>::quiet_NaN();
133 }
133 }
134 valuesData[valueIndex].append(value);
134 valuesData[valueIndex].append(value);
135 }
135 }
136 }
136 }
137 else {
137 else {
138 qCWarning(LOG_AmdaResultParser())
138 qCWarning(LOG_AmdaResultParser())
139 << QObject::tr("Can't retrieve results from line %1: x is invalid")
139 << QObject::tr("Can't retrieve results from line %1: x is invalid")
140 .arg(line);
140 .arg(line);
141 }
141 }
142 }
142 }
143 else {
143 else {
144 qCWarning(LOG_AmdaResultParser())
144 qCWarning(LOG_AmdaResultParser())
145 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
145 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
146 }
146 }
147 }
147 }
148 }
148 }
149
149
150 return qMakePair(std::move(xData), std::move(valuesData));
150 return qMakePair(std::move(xData), std::move(valuesData));
151 }
151 }
152
152
153 } // namespace
153 } // namespace
154
154
155 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
155 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath,
156 ValueType valueType) noexcept
156 ValueType valueType) noexcept
157 {
157 {
158 if (valueType == ValueType::UNKNOWN) {
158 if (valueType == ValueType::UNKNOWN) {
159 qCCritical(LOG_AmdaResultParser())
159 qCCritical(LOG_AmdaResultParser())
160 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
160 << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown");
161 return nullptr;
161 return nullptr;
162 }
162 }
163
163
164 QFile file{filePath};
164 QFile file{filePath};
165
165
166 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
166 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
167 qCCritical(LOG_AmdaResultParser())
167 qCCritical(LOG_AmdaResultParser())
168 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
168 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
169 .arg(filePath, file.errorString());
169 .arg(filePath, file.errorString());
170 return nullptr;
170 return nullptr;
171 }
171 }
172
172
173 QTextStream stream{&file};
173 QTextStream stream{&file};
174
174
175 // Checks if the file was found on the server
175 // Checks if the file was found on the server
176 auto firstLine = stream.readLine();
176 auto firstLine = stream.readLine();
177 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
177 if (firstLine.compare(FILE_NOT_FOUND_MESSAGE) == 0) {
178 qCCritical(LOG_AmdaResultParser())
178 qCCritical(LOG_AmdaResultParser())
179 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
179 << QObject::tr("Can't retrieve AMDA data from file %1: file was not found on server")
180 .arg(filePath);
180 .arg(filePath);
181 return nullptr;
181 return nullptr;
182 }
182 }
183
183
184 // Reads x-axis unit
184 // Reads x-axis unit
185 stream.seek(0); // returns to the beginning of the file
185 stream.seek(0); // returns to the beginning of the file
186 auto xAxisUnit = readXAxisUnit(stream);
186 auto xAxisUnit = readXAxisUnit(stream);
187
187
188 // Reads results
188 // Reads results
189 stream.seek(0); // returns to the beginning of the file
189 stream.seek(0); // returns to the beginning of the file
190 auto results = readResults(stream, valueType);
190 auto results = readResults(stream, valueType);
191
191
192 // Creates data series
192 // Creates data series
193 switch (valueType) {
193 switch (valueType) {
194 case ValueType::SCALAR:
194 case ValueType::SCALAR:
195 Q_ASSERT(results.second.size() == 1);
195 Q_ASSERT(results.second.size() == 1);
196 return std::make_shared<ScalarSeries>(
196 return std::make_shared<ScalarSeries>(
197 std::move(results.first), std::move(results.second.takeFirst()), xAxisUnit, Unit{});
197 std::move(results.first.toStdVector()),
198 std::move(results.second.takeFirst().toStdVector()), xAxisUnit, Unit{});
198 case ValueType::VECTOR: {
199 case ValueType::VECTOR: {
199 Q_ASSERT(results.second.size() == 3);
200 Q_ASSERT(results.second.size() == 3);
200 auto xValues = results.second.takeFirst();
201 auto xValues = results.second.takeFirst().toStdVector();
201 auto yValues = results.second.takeFirst();
202 auto yValues = results.second.takeFirst().toStdVector();
202 auto zValues = results.second.takeFirst();
203 auto zValues = results.second.takeFirst().toStdVector();
203 return std::make_shared<VectorSeries>(std::move(results.first), std::move(xValues),
204 return std::make_shared<VectorSeries>(std::move(results.first.toStdVector()),
204 std::move(yValues), std::move(zValues), xAxisUnit,
205 std::move(xValues), std::move(yValues),
205 Unit{});
206 std::move(zValues), xAxisUnit, Unit{});
206 }
207 }
207 case ValueType::UNKNOWN:
208 case ValueType::UNKNOWN:
208 // Invalid case
209 // Invalid case
209 break;
210 break;
210 }
211 }
211
212
212 // Invalid cases
213 // Invalid cases
213 qCCritical(LOG_AmdaResultParser())
214 qCCritical(LOG_AmdaResultParser())
214 << QObject::tr("Can't create data series: unsupported value type");
215 << QObject::tr("Can't create data series: unsupported value type");
215 return nullptr;
216 return nullptr;
216 }
217 }
@@ -1,103 +1,103
1 #include "CosinusProvider.h"
1 #include "CosinusProvider.h"
2
2
3 #include <Data/DataProviderParameters.h>
3 #include <Data/DataProviderParameters.h>
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5
5
6 #include <cmath>
6 #include <cmath>
7
7
8 #include <QFuture>
8 #include <QFuture>
9 #include <QThread>
9 #include <QThread>
10 #include <QtConcurrent/QtConcurrent>
10 #include <QtConcurrent/QtConcurrent>
11
11
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
12 Q_LOGGING_CATEGORY(LOG_CosinusProvider, "CosinusProvider")
13
13
14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
14 std::shared_ptr<IDataSeries> CosinusProvider::retrieveData(QUuid acqIdentifier,
15 const SqpRange &dataRangeRequested)
15 const SqpRange &dataRangeRequested)
16 {
16 {
17 // TODO: Add Mutex
17 // TODO: Add Mutex
18 auto dataIndex = 0;
18 auto dataIndex = 0;
19
19
20 // Gets the timerange from the parameters
20 // Gets the timerange from the parameters
21 double freq = 1.0;
21 double freq = 100.0;
22 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
22 double start = std::ceil(dataRangeRequested.m_TStart * freq); // 100 htz
23 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
23 double end = std::floor(dataRangeRequested.m_TEnd * freq); // 100 htz
24
24
25 // We assure that timerange is valid
25 // We assure that timerange is valid
26 if (end < start) {
26 if (end < start) {
27 std::swap(start, end);
27 std::swap(start, end);
28 }
28 }
29
29
30 // Generates scalar series containing cosinus values (one value per second)
30 // Generates scalar series containing cosinus values (one value per second)
31 auto dataCount = end - start;
31 auto dataCount = end - start;
32
32
33 auto xAxisData = QVector<double>{};
33 auto xAxisData = std::vector<double>{};
34 xAxisData.resize(dataCount);
34 xAxisData.resize(dataCount);
35
35
36 auto valuesData = QVector<double>{};
36 auto valuesData = std::vector<double>{};
37 valuesData.resize(dataCount);
37 valuesData.resize(dataCount);
38
38
39 int progress = 0;
39 int progress = 0;
40 auto progressEnd = dataCount;
40 auto progressEnd = dataCount;
41 for (auto time = start; time < end; ++time, ++dataIndex) {
41 for (auto time = start; time < end; ++time, ++dataIndex) {
42 auto it = m_VariableToEnableProvider.find(acqIdentifier);
42 auto it = m_VariableToEnableProvider.find(acqIdentifier);
43 if (it != m_VariableToEnableProvider.end() && it.value()) {
43 if (it != m_VariableToEnableProvider.end() && it.value()) {
44 const auto timeOnFreq = time / freq;
44 const auto timeOnFreq = time / freq;
45
45
46 xAxisData.replace(dataIndex, timeOnFreq);
46 xAxisData[dataIndex] = timeOnFreq;
47 valuesData.replace(dataIndex, std::cos(timeOnFreq));
47 valuesData[dataIndex] = std::cos(timeOnFreq);
48
48
49 // progression
49 // progression
50 int currentProgress = (time - start) * 100.0 / progressEnd;
50 int currentProgress = (time - start) * 100.0 / progressEnd;
51 if (currentProgress != progress) {
51 if (currentProgress != progress) {
52 progress = currentProgress;
52 progress = currentProgress;
53
53
54 emit dataProvidedProgress(acqIdentifier, progress);
54 emit dataProvidedProgress(acqIdentifier, progress);
55 }
55 }
56 }
56 }
57 else {
57 else {
58 if (!it.value()) {
58 if (!it.value()) {
59 qCDebug(LOG_CosinusProvider())
59 qCDebug(LOG_CosinusProvider())
60 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
60 << "CosinusProvider::retrieveData: ARRET De l'acquisition detectΓ©"
61 << end - time;
61 << end - time;
62 }
62 }
63 }
63 }
64 }
64 }
65 emit dataProvidedProgress(acqIdentifier, 0.0);
65 emit dataProvidedProgress(acqIdentifier, 0.0);
66
66
67 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
67 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
68 Unit{QStringLiteral("t"), true}, Unit{});
68 Unit{QStringLiteral("t"), true}, Unit{});
69 }
69 }
70
70
71 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
71 void CosinusProvider::requestDataLoading(QUuid acqIdentifier,
72 const DataProviderParameters &parameters)
72 const DataProviderParameters &parameters)
73 {
73 {
74 // TODO: Add Mutex
74 // TODO: Add Mutex
75 m_VariableToEnableProvider[acqIdentifier] = true;
75 m_VariableToEnableProvider[acqIdentifier] = true;
76 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
76 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::requestDataLoading"
77 << QThread::currentThread()->objectName();
77 << QThread::currentThread()->objectName();
78 // NOTE: Try to use multithread if possible
78 // NOTE: Try to use multithread if possible
79 const auto times = parameters.m_Times;
79 const auto times = parameters.m_Times;
80
80
81 for (const auto &dateTime : qAsConst(times)) {
81 for (const auto &dateTime : qAsConst(times)) {
82 if (m_VariableToEnableProvider[acqIdentifier]) {
82 if (m_VariableToEnableProvider[acqIdentifier]) {
83 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
83 auto scalarSeries = this->retrieveData(acqIdentifier, dateTime);
84 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
84 qCDebug(LOG_CosinusProvider()) << "TORM: CosinusProvider::dataProvided";
85 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
85 emit dataProvided(acqIdentifier, scalarSeries, dateTime);
86 }
86 }
87 }
87 }
88 }
88 }
89
89
90 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
90 void CosinusProvider::requestDataAborting(QUuid acqIdentifier)
91 {
91 {
92 // TODO: Add Mutex
92 // TODO: Add Mutex
93 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
93 qCDebug(LOG_CosinusProvider()) << "CosinusProvider::requestDataAborting" << acqIdentifier
94 << QThread::currentThread()->objectName();
94 << QThread::currentThread()->objectName();
95 auto it = m_VariableToEnableProvider.find(acqIdentifier);
95 auto it = m_VariableToEnableProvider.find(acqIdentifier);
96 if (it != m_VariableToEnableProvider.end()) {
96 if (it != m_VariableToEnableProvider.end()) {
97 it.value() = false;
97 it.value() = false;
98 }
98 }
99 else {
99 else {
100 qCWarning(LOG_CosinusProvider())
100 qCWarning(LOG_CosinusProvider())
101 << tr("Aborting progression of inexistant identifier detected !!!");
101 << tr("Aborting progression of inexistant identifier detected !!!");
102 }
102 }
103 }
103 }
General Comments 0
You need to be logged in to leave comments. Login now