##// END OF EJS Templates
Merge branch 'feature/UpdateArrayDataStruct' into develop
Alexandre Leroux -
r648:340220774853 merge
parent child
Show More
@@ -0,0 +1,56
1 #ifndef SCIQLOP_ARRAYDATAITERATOR_H
2 #define SCIQLOP_ARRAYDATAITERATOR_H
3
4 #include "CoreGlobal.h"
5 #include "Data/SqpIterator.h"
6
7 #include <memory>
8
9 /**
10 * @brief The ArrayDataIteratorValue class represents the current value of an array data iterator.
11 * It offers standard access methods for the data in the series (at(), first()), but it is up to
12 * each array data to define its own implementation of how to retrieve this data (one-dim or two-dim
13 * array), by implementing the ArrayDataIteratorValue::Impl interface
14 * @sa ArrayDataIterator
15 */
16 class SCIQLOP_CORE_EXPORT ArrayDataIteratorValue {
17 public:
18 struct Impl {
19 virtual ~Impl() noexcept = default;
20 virtual std::unique_ptr<Impl> clone() const = 0;
21 virtual bool equals(const Impl &other) const = 0;
22 virtual void next() = 0;
23 virtual void prev() = 0;
24 virtual double at(int componentIndex) const = 0;
25 virtual double first() const = 0;
26 virtual double min() const = 0;
27 virtual double max() const = 0;
28 };
29
30 explicit ArrayDataIteratorValue(std::unique_ptr<Impl> impl);
31 ArrayDataIteratorValue(const ArrayDataIteratorValue &other);
32 ArrayDataIteratorValue(ArrayDataIteratorValue &&other) = default;
33 ArrayDataIteratorValue &operator=(ArrayDataIteratorValue other);
34
35 bool equals(const ArrayDataIteratorValue &other) const;
36
37 /// Advances to the next value
38 void next();
39 /// Moves back to the previous value
40 void prev();
41 /// Gets value of a specified component
42 double at(int componentIndex) const;
43 /// Gets value of first component
44 double first() const;
45 /// Gets min value among all components
46 double min() const;
47 /// Gets max value among all components
48 double max() const;
49
50 private:
51 std::unique_ptr<Impl> m_Impl;
52 };
53
54 using ArrayDataIterator = SqpIterator<ArrayDataIteratorValue>;
55
56 #endif // SCIQLOP_ARRAYDATAITERATOR_H
@@ -0,0 +1,54
1 #ifndef SCIQLOP_SQPITERATOR_H
2 #define SCIQLOP_SQPITERATOR_H
3
4 #include "CoreGlobal.h"
5
6 /**
7 * @brief The SqpIterator class represents an iterator used in SciQlop. It defines all operators
8 * needed for a standard forward iterator
9 * @tparam T the type of object handled in iterator
10 * @sa http://www.cplusplus.com/reference/iterator/
11 */
12 template <typename T>
13 class SCIQLOP_CORE_EXPORT SqpIterator {
14 public:
15 using iterator_category = std::forward_iterator_tag;
16 using value_type = const T;
17 using difference_type = std::ptrdiff_t;
18 using pointer = value_type *;
19 using reference = value_type &;
20
21 explicit SqpIterator(T value) : m_CurrentValue{std::move(value)} {}
22
23 virtual ~SqpIterator() noexcept = default;
24 SqpIterator(const SqpIterator &) = default;
25 SqpIterator(SqpIterator &&) = default;
26 SqpIterator &operator=(const SqpIterator &) = default;
27 SqpIterator &operator=(SqpIterator &&) = default;
28
29 SqpIterator &operator++()
30 {
31 m_CurrentValue.next();
32 return *this;
33 }
34
35 SqpIterator &operator--()
36 {
37 m_CurrentValue.prev();
38 return *this;
39 }
40
41 pointer operator->() const { return &m_CurrentValue; }
42 reference operator*() const { return m_CurrentValue; }
43
44 bool operator==(const SqpIterator &other) const
45 {
46 return m_CurrentValue.equals(other.m_CurrentValue);
47 }
48 bool operator!=(const SqpIterator &other) const { return !(*this == other); }
49
50 private:
51 T m_CurrentValue;
52 };
53
54 #endif // SCIQLOP_SQPITERATOR_H
@@ -0,0 +1,52
1 #include "Data/ArrayDataIterator.h"
2
3 ArrayDataIteratorValue::ArrayDataIteratorValue(std::unique_ptr<ArrayDataIteratorValue::Impl> impl)
4 : m_Impl{std::move(impl)}
5 {
6 }
7
8 ArrayDataIteratorValue::ArrayDataIteratorValue(const ArrayDataIteratorValue &other)
9 : m_Impl{other.m_Impl->clone()}
10 {
11 }
12
13 ArrayDataIteratorValue &ArrayDataIteratorValue::operator=(ArrayDataIteratorValue other)
14 {
15 std::swap(m_Impl, other.m_Impl);
16 return *this;
17 }
18
19 bool ArrayDataIteratorValue::equals(const ArrayDataIteratorValue &other) const
20 {
21 return m_Impl->equals(*other.m_Impl);
22 }
23
24 void ArrayDataIteratorValue::next()
25 {
26 m_Impl->next();
27 }
28
29 void ArrayDataIteratorValue::prev()
30 {
31 m_Impl->prev();
32 }
33
34 double ArrayDataIteratorValue::at(int componentIndex) const
35 {
36 return m_Impl->at(componentIndex);
37 }
38
39 double ArrayDataIteratorValue::first() const
40 {
41 return m_Impl->first();
42 }
43
44 double ArrayDataIteratorValue::min() const
45 {
46 return m_Impl->min();
47 }
48
49 double ArrayDataIteratorValue::max() const
50 {
51 return m_Impl->max();
52 }
@@ -1,117 +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
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.
42 *
43 * Example 1:
44 * container: {1, 2, 3, 4, 5, 6}
45 * sortPermutation: {1, 0}
46 *
47 * Values will be sorted three by three, and the result will be:
48 * {4, 5, 6, 1, 2, 3}
49 *
50 * Example 2:
51 * container: {1, 2, 3, 4, 5, 6}
52 * sortPermutation: {2, 0, 1}
53 *
54 * Values will be sorted two by two, and the result will be:
55 * {5, 6, 1, 2, 3, 4}
56 *
41 * @param container the container sorted
57 * @param container the container sorted
42 * @param sortPermutation the indices used to sort the container
58 * @param sortPermutation the indices used to sort the container
43 * @return the container sorted
59 * @return the container sorted
44 * @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
45 * indices and its range is [0 ; vector.size()[ )
61 * indices and its range is [0 ; vector.size()[ )
46 */
62 */
47 template <typename Container>
63 template <typename Container>
48 static Container sort(const Container &container, const std::vector<int> &sortPermutation)
64 static Container sort(const Container &container, int nbValues,
65 const std::vector<int> &sortPermutation)
49 {
66 {
50 if (container.size() != sortPermutation.size()) {
67 auto containerSize = container.size();
68 if (containerSize % nbValues != 0
69 || ((containerSize / nbValues) != sortPermutation.size())) {
51 return Container{};
70 return Container{};
52 }
71 }
53
72
54 // Inits result
73 // Inits result
55 auto sortedData = Container{};
74 auto sortedData = Container{};
56 sortedData.resize(container.size());
75 sortedData.reserve(containerSize);
57
76
58 std::transform(sortPermutation.cbegin(), sortPermutation.cend(), sortedData.begin(),
77 for (auto i = 0, componentIndex = 0, permutationIndex = 0; i < containerSize;
59 [&container](int i) { return container.at(i); });
78 ++i, componentIndex = i % nbValues, permutationIndex = i / nbValues) {
79 auto insertIndex = sortPermutation.at(permutationIndex) * nbValues + componentIndex;
80 sortedData.append(container.at(insertIndex));
81 }
60
82
61 return sortedData;
83 return sortedData;
62 }
84 }
63
85
64 /**
86 /**
65 * 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
66 * for searching min value by excluding NaN values.
88 * for searching min value by excluding NaN values.
67 *
89 *
68 * Examples of use:
90 * Examples of use:
69 * - f({1, 3, 2, 4, 5}) will return 1
91 * - f({1, 3, 2, 4, 5}) will return 1
70 * - 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)
71 * - 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)
72 * - 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)
73 *
95 *
74 * @param v1 first value
96 * @param v1 first value
75 * @param v2 second value
97 * @param v2 second value
76 * @return true if v1 < v2, false otherwise
98 * @return true if v1 < v2, false otherwise
77 * @sa std::min_element
99 * @sa std::min_element
78 */
100 */
79 template <typename T>
101 template <typename T>
80 static bool minCompareWithNaN(const T &v1, const T &v2)
102 static bool minCompareWithNaN(const T &v1, const T &v2)
81 {
103 {
82 // Table used with NaN values:
104 // Table used with NaN values:
83 // NaN < v2 -> false
105 // NaN < v2 -> false
84 // v1 < NaN -> true
106 // v1 < NaN -> true
85 // NaN < NaN -> false
107 // NaN < NaN -> false
86 // v1 < v2 -> v1 < v2
108 // v1 < v2 -> v1 < v2
87 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
109 return std::isnan(v1) ? false : std::isnan(v2) || (v1 < v2);
88 }
110 }
89
111
90 /**
112 /**
91 * 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
92 * for searching max value by excluding NaN values.
114 * for searching max value by excluding NaN values.
93 *
115 *
94 * Examples of use:
116 * Examples of use:
95 * - f({1, 3, 2, 4, 5}) will return 5
117 * - f({1, 3, 2, 4, 5}) will return 5
96 * - 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)
97 * - 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)
98 * - 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)
99 *
121 *
100 * @param v1 first value
122 * @param v1 first value
101 * @param v2 second value
123 * @param v2 second value
102 * @return true if v1 < v2, false otherwise
124 * @return true if v1 < v2, false otherwise
103 * @sa std::max_element
125 * @sa std::max_element
104 */
126 */
105 template <typename T>
127 template <typename T>
106 static bool maxCompareWithNaN(const T &v1, const T &v2)
128 static bool maxCompareWithNaN(const T &v1, const T &v2)
107 {
129 {
108 // Table used with NaN values:
130 // Table used with NaN values:
109 // NaN < v2 -> true
131 // NaN < v2 -> true
110 // v1 < NaN -> false
132 // v1 < NaN -> false
111 // NaN < NaN -> false
133 // NaN < NaN -> false
112 // v1 < v2 -> v1 < v2
134 // v1 < v2 -> v1 < v2
113 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
135 return std::isnan(v1) ? true : !std::isnan(v2) && (v1 < v2);
114 }
136 }
115 };
137 };
116
138
117 #endif // SCIQLOP_SORTUTILS_H
139 #endif // SCIQLOP_SORTUTILS_H
@@ -1,348 +1,271
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 <Common/SortUtils.h>
5 #include <Common/SortUtils.h>
5
6
6 #include <QReadLocker>
7 #include <QReadLocker>
7 #include <QReadWriteLock>
8 #include <QReadWriteLock>
8 #include <QVector>
9 #include <QVector>
9
10
10 #include <memory>
11 #include <memory>
11
12
12 template <int Dim>
13 template <int Dim>
13 class ArrayData;
14 class ArrayData;
14
15
15 using DataContainer = QVector<QVector<double> >;
16 using DataContainer = QVector<double>;
16
17
17 namespace arraydata_detail {
18 namespace arraydata_detail {
18
19
19 /// Struct used to sort ArrayData
20 /// Struct used to sort ArrayData
20 template <int Dim>
21 template <int Dim>
21 struct Sort {
22 struct Sort {
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
23 const std::vector<int> &sortPermutation)
24 const std::vector<int> &sortPermutation)
24 {
25 {
25 auto nbComponents = data.size();
26 return std::make_shared<ArrayData<Dim> >(
26 auto sortedData = DataContainer(nbComponents);
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
27
28 for (auto i = 0; i < nbComponents; ++i) {
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
30 }
31
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
33 }
28 }
34 };
29 };
35
30
36 /// Specialization for uni-dimensional ArrayData
31 /// Specialization for uni-dimensional ArrayData
37 template <>
32 template <>
38 struct Sort<1> {
33 struct Sort<1> {
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
40 const std::vector<int> &sortPermutation)
35 const std::vector<int> &sortPermutation)
41 {
36 {
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
37 Q_UNUSED(nbComponents)
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
39 }
40 };
41
42 template <int Dim>
43 class IteratorValue : public ArrayDataIteratorValue::Impl {
44 public:
45 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
46 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
47 {
48 }
49
50 IteratorValue(const IteratorValue &other) = default;
51
52 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
53 {
54 return std::make_unique<IteratorValue<Dim> >(*this);
55 }
56
57 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
60 }
61 catch (const std::bad_cast &) {
62 return false;
63 }
64
65 void next() override { std::advance(m_It, m_NbComponents); }
66 void prev() override { std::advance(m_It, -m_NbComponents); }
67
68 double at(int componentIndex) const override { return *(m_It + componentIndex); }
69 double first() const override { return *m_It; }
70 double min() const override
71 {
72 auto values = this->values();
73 auto end = values.cend();
74 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
75 return SortUtils::minCompareWithNaN(v1, v2);
76 });
77
78 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
79 }
80 double max() const override
81 {
82 auto values = this->values();
83 auto end = values.cend();
84 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
85 return SortUtils::maxCompareWithNaN(v1, v2);
86 });
87 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
88 }
89
90 private:
91 std::vector<double> values() const
92 {
93 auto result = std::vector<double>{};
94 for (auto i = 0; i < m_NbComponents; ++i) {
95 result.push_back(*(m_It + i));
96 }
97
98 return result;
43 }
99 }
100
101 DataContainer::const_iterator m_It;
102 int m_NbComponents;
44 };
103 };
45
104
46 } // namespace arraydata_detail
105 } // namespace arraydata_detail
47
106
48 /**
107 /**
49 * @brief The ArrayData class represents a dataset for a data series.
108 * @brief The ArrayData class represents a dataset for a data series.
50 *
109 *
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
110 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
111 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
53 * number of values
112 * number of values
54 *
113 *
55 * @tparam Dim the dimension of the ArrayData (one or two)
114 * @tparam Dim the dimension of the ArrayData (one or two)
56 * @sa IDataSeries
115 * @sa IDataSeries
57 */
116 */
58 template <int Dim>
117 template <int Dim>
59 class ArrayData {
118 class ArrayData {
60 public:
119 public:
61 class IteratorValue {
62 public:
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
64 {
65 for (auto i = 0; i < container.size(); ++i) {
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
67 }
68 }
69
70 double at(int index) const { return *m_Its.at(index); }
71 double first() const { return *m_Its.front(); }
72
73 /// @return the min value among all components
74 double min() const
75 {
76 auto end = m_Its.cend();
77 auto it = std::min_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
78 return SortUtils::minCompareWithNaN(*it1, *it2);
79 });
80 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
81 }
82
83 /// @return the max value among all components
84 double max() const
85 {
86 auto end = m_Its.cend();
87 auto it = std::max_element(m_Its.cbegin(), end, [](const auto &it1, const auto &it2) {
88 return SortUtils::maxCompareWithNaN(*it1, *it2);
89 });
90 return it != end ? **it : std::numeric_limits<double>::quiet_NaN();
91 }
92
93 void next()
94 {
95 for (auto &it : m_Its) {
96 ++it;
97 }
98 }
99
100 void prev()
101 {
102 for (auto &it : m_Its) {
103 --it;
104 }
105 }
106
107 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
108
109 private:
110 std::vector<DataContainer::value_type::const_iterator> m_Its;
111 };
112
113 class Iterator {
114 public:
115 using iterator_category = std::forward_iterator_tag;
116 using value_type = const IteratorValue;
117 using difference_type = std::ptrdiff_t;
118 using pointer = value_type *;
119 using reference = value_type &;
120
121 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
122
123 virtual ~Iterator() noexcept = default;
124 Iterator(const Iterator &) = default;
125 Iterator(Iterator &&) = default;
126 Iterator &operator=(const Iterator &) = default;
127 Iterator &operator=(Iterator &&) = default;
128
129 Iterator &operator++()
130 {
131 m_CurrentValue.next();
132 return *this;
133 }
134
135 Iterator &operator--()
136 {
137 m_CurrentValue.prev();
138 return *this;
139 }
140
141 pointer operator->() const { return &m_CurrentValue; }
142 reference operator*() const { return m_CurrentValue; }
143
144 bool operator==(const Iterator &other) const
145 {
146 return m_CurrentValue == other.m_CurrentValue;
147 }
148
149 bool operator!=(const Iterator &other) const { return !(*this == other); }
150
151 private:
152 IteratorValue m_CurrentValue;
153 };
154
155 // ///// //
120 // ///// //
156 // Ctors //
121 // Ctors //
157 // ///// //
122 // ///// //
158
123
159 /**
124 /**
160 * Ctor for a unidimensional ArrayData
125 * Ctor for a unidimensional ArrayData
161 * @param data the data the ArrayData will hold
126 * @param data the data the ArrayData will hold
162 */
127 */
163 template <int D = Dim, typename = std::enable_if_t<D == 1> >
128 template <int D = Dim, typename = std::enable_if_t<D == 1> >
164 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
129 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
165 {
130 {
166 m_Data[0] = std::move(data);
167 }
131 }
168
132
169 /**
133 /**
170 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
134 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
171 * greater than 2 and each component must have the same number of values
135 * greater than 2 and must be a divisor of the total number of data in the vector
172 * @param data the data the ArrayData will hold
136 * @param data the data the ArrayData will hold
173 * @throws std::invalid_argument if the number of components is less than 2
137 * @param nbComponents the number of components
174 * @remarks if the number of values is not the same for each component, no value is set
138 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
139 * of the size of the data
175 */
140 */
176 template <int D = Dim, typename = std::enable_if_t<D == 2> >
141 template <int D = Dim, typename = std::enable_if_t<D == 2> >
177 explicit ArrayData(DataContainer data)
142 explicit ArrayData(DataContainer data, int nbComponents)
143 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
178 {
144 {
179 auto nbComponents = data.size();
180 if (nbComponents < 2) {
145 if (nbComponents < 2) {
181 throw std::invalid_argument{
146 throw std::invalid_argument{
182 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
147 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
183 .arg(data.size())
148 .arg(nbComponents)
184 .toStdString()};
149 .toStdString()};
185 }
150 }
186
151
187 auto nbValues = data.front().size();
152 if (m_Data.size() % m_NbComponents != 0) {
188 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
153 throw std::invalid_argument{QString{
189 return component.size() == nbValues;
154 "The number of components (%1) is inconsistent with the total number of data (%2)"}
190 })) {
155 .arg(m_Data.size(), nbComponents)
191 m_Data = std::move(data);
156 .toStdString()};
192 }
193 else {
194 m_Data = DataContainer{nbComponents, QVector<double>{}};
195 }
157 }
196 }
158 }
197
159
198 /// Copy ctor
160 /// Copy ctor
199 explicit ArrayData(const ArrayData &other)
161 explicit ArrayData(const ArrayData &other)
200 {
162 {
201 QReadLocker otherLocker{&other.m_Lock};
163 QReadLocker otherLocker{&other.m_Lock};
202 m_Data = other.m_Data;
164 m_Data = other.m_Data;
165 m_NbComponents = other.m_NbComponents;
203 }
166 }
204
167
205 // /////////////// //
168 // /////////////// //
206 // General methods //
169 // General methods //
207 // /////////////// //
170 // /////////////// //
208
171
209 /**
172 /**
210 * Merges into the array data an other array data. The two array datas must have the same number
173 * Merges into the array data an other array data. The two array datas must have the same number
211 * of components so the merge can be done
174 * of components so the merge can be done
212 * @param other the array data to merge with
175 * @param other the array data to merge with
213 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
176 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
214 * inserted at the end
177 * inserted at the end
215 */
178 */
216 void add(const ArrayData<Dim> &other, bool prepend = false)
179 void add(const ArrayData<Dim> &other, bool prepend = false)
217 {
180 {
218 QWriteLocker locker{&m_Lock};
181 QWriteLocker locker{&m_Lock};
219 QReadLocker otherLocker{&other.m_Lock};
182 QReadLocker otherLocker{&other.m_Lock};
220
183
221 auto nbComponents = m_Data.size();
184 if (m_NbComponents != other.componentCount()) {
222 if (nbComponents != other.m_Data.size()) {
223 return;
185 return;
224 }
186 }
225
187
226 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
188 if (prepend) {
227 if (prepend) {
189 auto otherDataSize = other.m_Data.size();
228 const auto &otherData = other.data(componentIndex);
190 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
229 const auto otherDataSize = otherData.size();
191 for (auto i = 0; i < otherDataSize; ++i) {
230
192 m_Data.replace(i, other.m_Data.at(i));
231 auto &data = m_Data[componentIndex];
232 data.insert(data.begin(), otherDataSize, 0.);
233
234 for (auto i = 0; i < otherDataSize; ++i) {
235 data.replace(i, otherData.at(i));
236 }
237 }
238 else {
239 m_Data[componentIndex] += other.data(componentIndex);
240 }
193 }
241 }
194 }
195 else {
196 m_Data.append(other.m_Data);
197 }
242 }
198 }
243
199
244 void clear()
200 void clear()
245 {
201 {
246 QWriteLocker locker{&m_Lock};
202 QWriteLocker locker{&m_Lock};
247
203 m_Data.clear();
248 auto nbComponents = m_Data.size();
249 for (auto i = 0; i < nbComponents; ++i) {
250 m_Data[i].clear();
251 }
252 }
204 }
253
205
254 int componentCount() const noexcept { return m_Data.size(); }
206 int componentCount() const noexcept { return m_NbComponents; }
255
256 /**
257 * @return the data of a component
258 * @param componentIndex the index of the component to retrieve the data
259 * @return the component's data, empty vector if the index is invalid
260 */
261 QVector<double> data(int componentIndex) const noexcept
262 {
263 QReadLocker locker{&m_Lock};
264
265 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
266 : QVector<double>{};
267 }
268
207
269 /// @return the size (i.e. number of values) of a single component
208 /// @return the size (i.e. number of values) of a single component
270 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
209 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
271 int size() const
210 int size() const
272 {
211 {
273 QReadLocker locker{&m_Lock};
212 QReadLocker locker{&m_Lock};
274 return m_Data[0].size();
213 return m_Data.size() / m_NbComponents;
275 }
214 }
276
215
277 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
216 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
278 {
217 {
279 QReadLocker locker{&m_Lock};
218 QReadLocker locker{&m_Lock};
280 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
219 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
281 }
220 }
282
221
283 // ///////// //
222 // ///////// //
284 // Iterators //
223 // Iterators //
285 // ///////// //
224 // ///////// //
286
225
287 Iterator cbegin() const { return Iterator{m_Data, true}; }
226 ArrayDataIterator cbegin() const
288 Iterator cend() const { return Iterator{m_Data, false}; }
227 {
228 return ArrayDataIterator{ArrayDataIteratorValue{
229 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, m_NbComponents, true)}};
230 }
231 ArrayDataIterator cend() const
232 {
233 return ArrayDataIterator{
234 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim> >(
235 m_Data, m_NbComponents, false)}};
236 }
289
237
290 // ///////////// //
291 // 1-dim methods //
292 // ///////////// //
293
238
294 /**
239 /**
295 * @return the data at a specified index
240 * @return the data at a specified index
296 * @remarks index must be a valid position
241 * @remarks index must be a valid position
297 * @remarks this method is only available for a unidimensional ArrayData
298 */
242 */
299 template <int D = Dim, typename = std::enable_if_t<D == 1> >
300 double at(int index) const noexcept
243 double at(int index) const noexcept
301 {
244 {
302 QReadLocker locker{&m_Lock};
245 QReadLocker locker{&m_Lock};
303 return m_Data[0].at(index);
246 return m_Data.at(index);
304 }
247 }
305
248
249 // ///////////// //
250 // 1-dim methods //
251 // ///////////// //
252
306 /**
253 /**
307 * @return the data as a vector, as a const reference
254 * @return the data as a vector, as a const reference
308 * @remarks this method is only available for a unidimensional ArrayData
255 * @remarks this method is only available for a unidimensional ArrayData
309 */
256 */
310 template <int D = Dim, typename = std::enable_if_t<D == 1> >
257 template <int D = Dim, typename = std::enable_if_t<D == 1> >
311 const QVector<double> &cdata() const noexcept
258 const QVector<double> &cdata() const noexcept
312 {
259 {
313 QReadLocker locker{&m_Lock};
260 QReadLocker locker{&m_Lock};
314 return m_Data.at(0);
315 }
316
317 /**
318 * @return the data as a vector
319 * @remarks this method is only available for a unidimensional ArrayData
320 */
321 template <int D = Dim, typename = std::enable_if_t<D == 1> >
322 QVector<double> data() const noexcept
323 {
324 QReadLocker locker{&m_Lock};
325 return m_Data[0];
326 }
327
328 // ///////////// //
329 // 2-dim methods //
330 // ///////////// //
331
332 /**
333 * @return the data
334 * @remarks this method is only available for a two-dimensional ArrayData
335 */
336 template <int D = Dim, typename = std::enable_if_t<D == 2> >
337 DataContainer data() const noexcept
338 {
339 QReadLocker locker{&m_Lock};
340 return m_Data;
261 return m_Data;
341 }
262 }
342
263
343 private:
264 private:
344 DataContainer m_Data;
265 DataContainer m_Data;
266 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
267 int m_NbComponents;
345 mutable QReadWriteLock m_Lock;
268 mutable QReadWriteLock m_Lock;
346 };
269 };
347
270
348 #endif // SCIQLOP_ARRAYDATA_H
271 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,333 +1,333
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/IDataSeries.h>
9 #include <Data/IDataSeries.h>
10
10
11 #include <QLoggingCategory>
11 #include <QLoggingCategory>
12 #include <QReadLocker>
12 #include <QReadLocker>
13 #include <QReadWriteLock>
13 #include <QReadWriteLock>
14 #include <memory>
14 #include <memory>
15
15
16 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
16 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
17 // definitions with inheritance. Inline method is used instead
17 // definitions with inheritance. Inline method is used instead
18 inline const QLoggingCategory &LOG_DataSeries()
18 inline const QLoggingCategory &LOG_DataSeries()
19 {
19 {
20 static const QLoggingCategory category{"DataSeries"};
20 static const QLoggingCategory category{"DataSeries"};
21 return category;
21 return category;
22 }
22 }
23
23
24 template <int Dim>
24 template <int Dim>
25 class DataSeries;
25 class DataSeries;
26
26
27 namespace dataseries_detail {
27 namespace dataseries_detail {
28
28
29 template <int Dim>
29 template <int Dim>
30 class IteratorValue : public DataSeriesIteratorValue::Impl {
30 class IteratorValue : public DataSeriesIteratorValue::Impl {
31 public:
31 public:
32 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
32 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
33 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
33 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
34 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
34 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
35 : dataSeries.valuesData()->cend())
35 : dataSeries.valuesData()->cend())
36 {
36 {
37 }
37 }
38 IteratorValue(const IteratorValue &other) = default;
38 IteratorValue(const IteratorValue &other) = default;
39
39
40 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
40 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
41 {
41 {
42 return std::make_unique<IteratorValue<Dim> >(*this);
42 return std::make_unique<IteratorValue<Dim> >(*this);
43 }
43 }
44
44
45 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
45 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
46 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
46 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
47 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
47 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
48 }
48 }
49 catch (const std::bad_cast &) {
49 catch (const std::bad_cast &) {
50 return false;
50 return false;
51 }
51 }
52
52
53 void next() override
53 void next() override
54 {
54 {
55 ++m_XIt;
55 ++m_XIt;
56 ++m_ValuesIt;
56 ++m_ValuesIt;
57 }
57 }
58
58
59 void prev() override
59 void prev() override
60 {
60 {
61 --m_XIt;
61 --m_XIt;
62 --m_ValuesIt;
62 --m_ValuesIt;
63 }
63 }
64
64
65 double x() const override { return m_XIt->at(0); }
65 double x() const override { return m_XIt->at(0); }
66 double value() const override { return m_ValuesIt->at(0); }
66 double value() const override { return m_ValuesIt->at(0); }
67 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
67 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
68 double minValue() const override { return m_ValuesIt->min(); }
68 double minValue() const override { return m_ValuesIt->min(); }
69 double maxValue() const override { return m_ValuesIt->max(); }
69 double maxValue() const override { return m_ValuesIt->max(); }
70
70
71 private:
71 private:
72 ArrayData<1>::Iterator m_XIt;
72 ArrayDataIterator m_XIt;
73 typename ArrayData<Dim>::Iterator m_ValuesIt;
73 ArrayDataIterator m_ValuesIt;
74 };
74 };
75 } // namespace dataseries_detail
75 } // namespace dataseries_detail
76
76
77 /**
77 /**
78 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
78 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
79 *
79 *
80 * It proposes to set a dimension for the values ​​data.
80 * It proposes to set a dimension for the values ​​data.
81 *
81 *
82 * A DataSeries is always sorted on its x-axis data.
82 * A DataSeries is always sorted on its x-axis data.
83 *
83 *
84 * @tparam Dim The dimension of the values data
84 * @tparam Dim The dimension of the values data
85 *
85 *
86 */
86 */
87 template <int Dim>
87 template <int Dim>
88 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
88 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
89 public:
89 public:
90 /// @sa IDataSeries::xAxisData()
90 /// @sa IDataSeries::xAxisData()
91 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
91 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
92 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
92 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
93
93
94 /// @sa IDataSeries::xAxisUnit()
94 /// @sa IDataSeries::xAxisUnit()
95 Unit xAxisUnit() const override { return m_XAxisUnit; }
95 Unit xAxisUnit() const override { return m_XAxisUnit; }
96
96
97 /// @return the values dataset
97 /// @return the values dataset
98 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
98 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
99 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
99 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
100
100
101 /// @sa IDataSeries::valuesUnit()
101 /// @sa IDataSeries::valuesUnit()
102 Unit valuesUnit() const override { return m_ValuesUnit; }
102 Unit valuesUnit() const override { return m_ValuesUnit; }
103
103
104
104
105 SqpRange range() const override
105 SqpRange range() const override
106 {
106 {
107 if (!m_XAxisData->cdata().isEmpty()) {
107 if (!m_XAxisData->cdata().isEmpty()) {
108 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
108 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
109 }
109 }
110
110
111 return SqpRange{};
111 return SqpRange{};
112 }
112 }
113
113
114 void clear()
114 void clear()
115 {
115 {
116 m_XAxisData->clear();
116 m_XAxisData->clear();
117 m_ValuesData->clear();
117 m_ValuesData->clear();
118 }
118 }
119
119
120 /// Merges into the data series an other data series
120 /// Merges into the data series an other data series
121 /// @remarks the data series to merge with is cleared after the operation
121 /// @remarks the data series to merge with is cleared after the operation
122 void merge(IDataSeries *dataSeries) override
122 void merge(IDataSeries *dataSeries) override
123 {
123 {
124 dataSeries->lockWrite();
124 dataSeries->lockWrite();
125 lockWrite();
125 lockWrite();
126
126
127 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
127 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
128 const auto &otherXAxisData = other->xAxisData()->cdata();
128 const auto &otherXAxisData = other->xAxisData()->cdata();
129 const auto &xAxisData = m_XAxisData->cdata();
129 const auto &xAxisData = m_XAxisData->cdata();
130
130
131 // As data series are sorted, we can improve performances of merge, by call the sort
131 // As data series are sorted, we can improve performances of merge, by call the sort
132 // method only if the two data series overlap.
132 // method only if the two data series overlap.
133 if (!otherXAxisData.empty()) {
133 if (!otherXAxisData.empty()) {
134 auto firstValue = otherXAxisData.front();
134 auto firstValue = otherXAxisData.front();
135 auto lastValue = otherXAxisData.back();
135 auto lastValue = otherXAxisData.back();
136
136
137 auto xAxisDataBegin = xAxisData.cbegin();
137 auto xAxisDataBegin = xAxisData.cbegin();
138 auto xAxisDataEnd = xAxisData.cend();
138 auto xAxisDataEnd = xAxisData.cend();
139
139
140 bool prepend;
140 bool prepend;
141 bool sortNeeded;
141 bool sortNeeded;
142
142
143 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
143 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
144 // Other data series if after data series
144 // Other data series if after data series
145 prepend = false;
145 prepend = false;
146 sortNeeded = false;
146 sortNeeded = false;
147 }
147 }
148 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
148 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
149 == xAxisDataBegin) {
149 == xAxisDataBegin) {
150 // Other data series if before data series
150 // Other data series if before data series
151 prepend = true;
151 prepend = true;
152 sortNeeded = false;
152 sortNeeded = false;
153 }
153 }
154 else {
154 else {
155 // The two data series overlap
155 // The two data series overlap
156 prepend = false;
156 prepend = false;
157 sortNeeded = true;
157 sortNeeded = true;
158 }
158 }
159
159
160 // Makes the merge
160 // Makes the merge
161 m_XAxisData->add(*other->xAxisData(), prepend);
161 m_XAxisData->add(*other->xAxisData(), prepend);
162 m_ValuesData->add(*other->valuesData(), prepend);
162 m_ValuesData->add(*other->valuesData(), prepend);
163
163
164 if (sortNeeded) {
164 if (sortNeeded) {
165 sort();
165 sort();
166 }
166 }
167 }
167 }
168
168
169 // Clears the other data series
169 // Clears the other data series
170 other->clear();
170 other->clear();
171 }
171 }
172 else {
172 else {
173 qCWarning(LOG_DataSeries())
173 qCWarning(LOG_DataSeries())
174 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
174 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
175 }
175 }
176 unlock();
176 unlock();
177 dataSeries->unlock();
177 dataSeries->unlock();
178 }
178 }
179
179
180 // ///////// //
180 // ///////// //
181 // Iterators //
181 // Iterators //
182 // ///////// //
182 // ///////// //
183
183
184 DataSeriesIterator cbegin() const override
184 DataSeriesIterator cbegin() const override
185 {
185 {
186 return DataSeriesIterator{DataSeriesIteratorValue{
186 return DataSeriesIterator{DataSeriesIteratorValue{
187 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
187 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
188 }
188 }
189
189
190 DataSeriesIterator cend() const override
190 DataSeriesIterator cend() const override
191 {
191 {
192 return DataSeriesIterator{DataSeriesIteratorValue{
192 return DataSeriesIterator{DataSeriesIteratorValue{
193 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
193 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
194 }
194 }
195
195
196 /// @sa IDataSeries::minXAxisData()
196 /// @sa IDataSeries::minXAxisData()
197 DataSeriesIterator minXAxisData(double minXAxisData) const override
197 DataSeriesIterator minXAxisData(double minXAxisData) const override
198 {
198 {
199 return std::lower_bound(
199 return std::lower_bound(
200 cbegin(), cend(), minXAxisData,
200 cbegin(), cend(), minXAxisData,
201 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
201 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
202 }
202 }
203
203
204 /// @sa IDataSeries::maxXAxisData()
204 /// @sa IDataSeries::maxXAxisData()
205 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
205 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
206 {
206 {
207 // Gets the first element that greater than max value
207 // Gets the first element that greater than max value
208 auto it = std::upper_bound(
208 auto it = std::upper_bound(
209 cbegin(), cend(), maxXAxisData,
209 cbegin(), cend(), maxXAxisData,
210 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
210 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
211
211
212 return it == cbegin() ? cend() : --it;
212 return it == cbegin() ? cend() : --it;
213 }
213 }
214
214
215 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
215 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
216 double maxXAxisData) const override
216 double maxXAxisData) const override
217 {
217 {
218 if (minXAxisData > maxXAxisData) {
218 if (minXAxisData > maxXAxisData) {
219 std::swap(minXAxisData, maxXAxisData);
219 std::swap(minXAxisData, maxXAxisData);
220 }
220 }
221
221
222 auto begin = cbegin();
222 auto begin = cbegin();
223 auto end = cend();
223 auto end = cend();
224
224
225 auto lowerIt = std::lower_bound(
225 auto lowerIt = std::lower_bound(
226 begin, end, minXAxisData,
226 begin, end, minXAxisData,
227 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
227 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
228 auto upperIt = std::upper_bound(
228 auto upperIt = std::upper_bound(
229 begin, end, maxXAxisData,
229 begin, end, maxXAxisData,
230 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
230 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
231
231
232 return std::make_pair(lowerIt, upperIt);
232 return std::make_pair(lowerIt, upperIt);
233 }
233 }
234
234
235 std::pair<DataSeriesIterator, DataSeriesIterator>
235 std::pair<DataSeriesIterator, DataSeriesIterator>
236 valuesBounds(double minXAxisData, double maxXAxisData) const override
236 valuesBounds(double minXAxisData, double maxXAxisData) const override
237 {
237 {
238 // Places iterators to the correct x-axis range
238 // Places iterators to the correct x-axis range
239 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
239 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
240
240
241 // Returns end iterators if the range is empty
241 // Returns end iterators if the range is empty
242 if (xAxisRangeIts.first == xAxisRangeIts.second) {
242 if (xAxisRangeIts.first == xAxisRangeIts.second) {
243 return std::make_pair(cend(), cend());
243 return std::make_pair(cend(), cend());
244 }
244 }
245
245
246 // Gets the iterator on the min of all values data
246 // Gets the iterator on the min of all values data
247 auto minIt = std::min_element(
247 auto minIt = std::min_element(
248 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
248 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
249 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
249 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
250 });
250 });
251
251
252 // Gets the iterator on the max of all values data
252 // Gets the iterator on the max of all values data
253 auto maxIt = std::max_element(
253 auto maxIt = std::max_element(
254 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
254 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
255 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
255 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
256 });
256 });
257
257
258 return std::make_pair(minIt, maxIt);
258 return std::make_pair(minIt, maxIt);
259 }
259 }
260
260
261 // /////// //
261 // /////// //
262 // Mutexes //
262 // Mutexes //
263 // /////// //
263 // /////// //
264
264
265 virtual void lockRead() { m_Lock.lockForRead(); }
265 virtual void lockRead() { m_Lock.lockForRead(); }
266 virtual void lockWrite() { m_Lock.lockForWrite(); }
266 virtual void lockWrite() { m_Lock.lockForWrite(); }
267 virtual void unlock() { m_Lock.unlock(); }
267 virtual void unlock() { m_Lock.unlock(); }
268
268
269 protected:
269 protected:
270 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
270 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
271 /// DataSeries with no values will be created.
271 /// DataSeries with no values will be created.
272 /// @remarks data series is automatically sorted on its x-axis data
272 /// @remarks data series is automatically sorted on its x-axis data
273 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
273 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
274 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
274 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
275 : m_XAxisData{xAxisData},
275 : m_XAxisData{xAxisData},
276 m_XAxisUnit{xAxisUnit},
276 m_XAxisUnit{xAxisUnit},
277 m_ValuesData{valuesData},
277 m_ValuesData{valuesData},
278 m_ValuesUnit{valuesUnit}
278 m_ValuesUnit{valuesUnit}
279 {
279 {
280 if (m_XAxisData->size() != m_ValuesData->size()) {
280 if (m_XAxisData->size() != m_ValuesData->size()) {
281 clear();
281 clear();
282 }
282 }
283
283
284 // Sorts data if it's not the case
284 // Sorts data if it's not the case
285 const auto &xAxisCData = m_XAxisData->cdata();
285 const auto &xAxisCData = m_XAxisData->cdata();
286 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
286 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
287 sort();
287 sort();
288 }
288 }
289 }
289 }
290
290
291 /// Copy ctor
291 /// Copy ctor
292 explicit DataSeries(const DataSeries<Dim> &other)
292 explicit DataSeries(const DataSeries<Dim> &other)
293 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
293 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
294 m_XAxisUnit{other.m_XAxisUnit},
294 m_XAxisUnit{other.m_XAxisUnit},
295 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
295 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
296 m_ValuesUnit{other.m_ValuesUnit}
296 m_ValuesUnit{other.m_ValuesUnit}
297 {
297 {
298 // Since a series is ordered from its construction and is always ordered, it is not
298 // Since a series is ordered from its construction and is always ordered, it is not
299 // necessary to call the sort method here ('other' is sorted)
299 // necessary to call the sort method here ('other' is sorted)
300 }
300 }
301
301
302 /// Assignment operator
302 /// Assignment operator
303 template <int D>
303 template <int D>
304 DataSeries &operator=(DataSeries<D> other)
304 DataSeries &operator=(DataSeries<D> other)
305 {
305 {
306 std::swap(m_XAxisData, other.m_XAxisData);
306 std::swap(m_XAxisData, other.m_XAxisData);
307 std::swap(m_XAxisUnit, other.m_XAxisUnit);
307 std::swap(m_XAxisUnit, other.m_XAxisUnit);
308 std::swap(m_ValuesData, other.m_ValuesData);
308 std::swap(m_ValuesData, other.m_ValuesData);
309 std::swap(m_ValuesUnit, other.m_ValuesUnit);
309 std::swap(m_ValuesUnit, other.m_ValuesUnit);
310
310
311 return *this;
311 return *this;
312 }
312 }
313
313
314 private:
314 private:
315 /**
315 /**
316 * Sorts data series on its x-axis data
316 * Sorts data series on its x-axis data
317 */
317 */
318 void sort() noexcept
318 void sort() noexcept
319 {
319 {
320 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
320 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
321 m_XAxisData = m_XAxisData->sort(permutation);
321 m_XAxisData = m_XAxisData->sort(permutation);
322 m_ValuesData = m_ValuesData->sort(permutation);
322 m_ValuesData = m_ValuesData->sort(permutation);
323 }
323 }
324
324
325 std::shared_ptr<ArrayData<1> > m_XAxisData;
325 std::shared_ptr<ArrayData<1> > m_XAxisData;
326 Unit m_XAxisUnit;
326 Unit m_XAxisUnit;
327 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
327 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
328 Unit m_ValuesUnit;
328 Unit m_ValuesUnit;
329
329
330 QReadWriteLock m_Lock;
330 QReadWriteLock m_Lock;
331 };
331 };
332
332
333 #endif // SCIQLOP_DATASERIES_H
333 #endif // SCIQLOP_DATASERIES_H
@@ -1,88 +1,60
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
6
6 #include <memory>
7 #include <memory>
7
8
8 /**
9 /**
9 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
10 * @brief The DataSeriesIteratorValue class represents the current value of a data series iterator.
10 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
11 * It offers standard access methods for the data in the series (x-axis, values), but it is up to
11 * each series to define its own implementation of how to retrieve this data, by implementing the
12 * each series to define its own implementation of how to retrieve this data, by implementing the
12 * DataSeriesIteratorValue::Impl interface
13 * DataSeriesIteratorValue::Impl interface
13 *
14 *
14 * @sa DataSeriesIterator
15 * @sa DataSeriesIterator
15 */
16 */
16 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
17 class SCIQLOP_CORE_EXPORT DataSeriesIteratorValue {
17 public:
18 public:
18 struct Impl {
19 struct Impl {
19 virtual ~Impl() noexcept = default;
20 virtual ~Impl() noexcept = default;
20 virtual std::unique_ptr<Impl> clone() const = 0;
21 virtual std::unique_ptr<Impl> clone() const = 0;
21 virtual bool equals(const Impl &other) const = 0;
22 virtual bool equals(const Impl &other) const = 0;
22 virtual void next() = 0;
23 virtual void next() = 0;
23 virtual void prev() = 0;
24 virtual void prev() = 0;
24 virtual double x() const = 0;
25 virtual double x() const = 0;
25 virtual double value() const = 0;
26 virtual double value() const = 0;
26 virtual double value(int componentIndex) const = 0;
27 virtual double value(int componentIndex) const = 0;
27 virtual double minValue() const = 0;
28 virtual double minValue() const = 0;
28 virtual double maxValue() const = 0;
29 virtual double maxValue() const = 0;
29 };
30 };
30
31
31 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
32 explicit DataSeriesIteratorValue(std::unique_ptr<Impl> impl);
32 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
33 DataSeriesIteratorValue(const DataSeriesIteratorValue &other);
33 DataSeriesIteratorValue(DataSeriesIteratorValue &&other) = default;
34 DataSeriesIteratorValue(DataSeriesIteratorValue &&other) = default;
34 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
35 DataSeriesIteratorValue &operator=(DataSeriesIteratorValue other);
35
36
36 bool equals(const DataSeriesIteratorValue &other) const;
37 bool equals(const DataSeriesIteratorValue &other) const;
37
38
38 /// Advances to the next value
39 /// Advances to the next value
39 void next();
40 void next();
40 /// Moves back to the previous value
41 /// Moves back to the previous value
41 void prev();
42 void prev();
42 /// Gets x-axis data
43 /// Gets x-axis data
43 double x() const;
44 double x() const;
44 /// Gets value data
45 /// Gets value data
45 double value() const;
46 double value() const;
46 /// Gets value data depending on an index
47 /// Gets value data depending on an index
47 double value(int componentIndex) const;
48 double value(int componentIndex) const;
48 /// Gets min of all values data
49 /// Gets min of all values data
49 double minValue() const;
50 double minValue() const;
50 /// Gets max of all values data
51 /// Gets max of all values data
51 double maxValue() const;
52 double maxValue() const;
52
53
53 private:
54 private:
54 std::unique_ptr<Impl> m_Impl;
55 std::unique_ptr<Impl> m_Impl;
55 };
56 };
56
57
57 /**
58 using DataSeriesIterator = SqpIterator<DataSeriesIteratorValue>;
58 * @brief The DataSeriesIterator class represents an iterator used for data series. It defines all
59 * operators needed for a standard forward iterator
60 * @sa http://www.cplusplus.com/reference/iterator/
61 */
62 class SCIQLOP_CORE_EXPORT DataSeriesIterator {
63 public:
64 using iterator_category = std::forward_iterator_tag;
65 using value_type = const DataSeriesIteratorValue;
66 using difference_type = std::ptrdiff_t;
67 using pointer = value_type *;
68 using reference = value_type &;
69
70 explicit DataSeriesIterator(DataSeriesIteratorValue value);
71 virtual ~DataSeriesIterator() noexcept = default;
72 DataSeriesIterator(const DataSeriesIterator &) = default;
73 DataSeriesIterator(DataSeriesIterator &&) = default;
74 DataSeriesIterator &operator=(const DataSeriesIterator &) = default;
75 DataSeriesIterator &operator=(DataSeriesIterator &&) = default;
76
77 DataSeriesIterator &operator++();
78 DataSeriesIterator &operator--();
79 pointer operator->() const { return &m_CurrentValue; }
80 reference operator*() const { return m_CurrentValue; }
81 bool operator==(const DataSeriesIterator &other) const;
82 bool operator!=(const DataSeriesIterator &other) const;
83
84 private:
85 DataSeriesIteratorValue m_CurrentValue;
86 };
87
59
88 #endif // SCIQLOP_DATASERIESITERATOR_H
60 #endif // SCIQLOP_DATASERIESITERATOR_H
@@ -1,30 +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. The vectors must have the same size, otherwise a ScalarSeries with no values will be
14 * Ctor with three vectors (one per component). The vectors must have the same size, otherwise a
15 * 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(QVector<double> xAxisData, QVector<double> xValuesData,
22 QVector<double> yValuesData, QVector<double> zValuesData,
22 QVector<double> yValuesData, QVector<double> zValuesData,
23 const Unit &xAxisUnit, const Unit &valuesUnit);
23 const Unit &xAxisUnit, const Unit &valuesUnit);
24
24
25 /// Default Ctor
26 explicit VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
27 const Unit &xAxisUnit, const Unit &valuesUnit);
28
25 std::unique_ptr<IDataSeries> clone() const;
29 std::unique_ptr<IDataSeries> clone() const;
26
30
27 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
31 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
28 };
32 };
29
33
30 #endif // SCIQLOP_VECTORSERIES_H
34 #endif // SCIQLOP_VECTORSERIES_H
@@ -1,85 +1,58
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 std::swap(m_Impl, other.m_Impl);
16 std::swap(m_Impl, other.m_Impl);
17 return *this;
17 return *this;
18 }
18 }
19
19
20 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
20 bool DataSeriesIteratorValue::equals(const DataSeriesIteratorValue &other) const
21 {
21 {
22 return m_Impl->equals(*other.m_Impl);
22 return m_Impl->equals(*other.m_Impl);
23 }
23 }
24
24
25 void DataSeriesIteratorValue::next()
25 void DataSeriesIteratorValue::next()
26 {
26 {
27 m_Impl->next();
27 m_Impl->next();
28 }
28 }
29
29
30 void DataSeriesIteratorValue::prev()
30 void DataSeriesIteratorValue::prev()
31 {
31 {
32 m_Impl->prev();
32 m_Impl->prev();
33 }
33 }
34
34
35 double DataSeriesIteratorValue::x() const
35 double DataSeriesIteratorValue::x() const
36 {
36 {
37 return m_Impl->x();
37 return m_Impl->x();
38 }
38 }
39
39
40 double DataSeriesIteratorValue::value() const
40 double DataSeriesIteratorValue::value() const
41 {
41 {
42 return m_Impl->value();
42 return m_Impl->value();
43 }
43 }
44
44
45 double DataSeriesIteratorValue::value(int componentIndex) const
45 double DataSeriesIteratorValue::value(int componentIndex) const
46 {
46 {
47 return m_Impl->value(componentIndex);
47 return m_Impl->value(componentIndex);
48 }
48 }
49
49
50 double DataSeriesIteratorValue::minValue() const
50 double DataSeriesIteratorValue::minValue() const
51 {
51 {
52 return m_Impl->minValue();
52 return m_Impl->minValue();
53 }
53 }
54
54
55 double DataSeriesIteratorValue::maxValue() const
55 double DataSeriesIteratorValue::maxValue() const
56 {
56 {
57 return m_Impl->maxValue();
57 return m_Impl->maxValue();
58 }
58 }
59
60 DataSeriesIterator::DataSeriesIterator(DataSeriesIteratorValue value)
61 : m_CurrentValue{std::move(value)}
62 {
63 }
64
65 DataSeriesIterator &DataSeriesIterator::operator++()
66 {
67 m_CurrentValue.next();
68 return *this;
69 }
70
71 DataSeriesIterator &DataSeriesIterator::operator--()
72 {
73 m_CurrentValue.prev();
74 return *this;
75 }
76
77 bool DataSeriesIterator::operator==(const DataSeriesIterator &other) const
78 {
79 return m_CurrentValue.equals(other.m_CurrentValue);
80 }
81
82 bool DataSeriesIterator::operator!=(const DataSeriesIterator &other) const
83 {
84 return !(*this == other);
85 }
@@ -1,39 +1,83
1 #include "Data/VectorSeries.h"
1 #include "Data/VectorSeries.h"
2
2
3 namespace {
4
5 /**
6 * Flatten the three components of a vector to a single QVector that can be passed to an ArrayData
7 *
8 * Example:
9 * xValues = {1, 2, 3}
10 * yValues = {4, 5, 6}
11 * zValues = {7, 8, 9}
12 *
13 * result = {1, 4, 7, 2, 5, 8, 3, 6, 9}
14 *
15 * @param xValues the x-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
18 * @return the single QVector
19 * @remarks the three components are consumed
20 * @sa ArrayData
21 */
22 QVector<double> flatten(QVector<double> xValues, QVector<double> yValues, QVector<double> zValues)
23 {
24 if (xValues.size() != yValues.size() || xValues.size() != zValues.size()) {
25 /// @todo ALX : log
26 return {};
27 }
28
29 auto result = QVector<double>{};
30 result.reserve(xValues.size() * 3);
31
32 while (!xValues.isEmpty()) {
33 result.append({xValues.takeFirst(), yValues.takeFirst(), zValues.takeFirst()});
34 }
35
36 return result;
37 }
38
39 } // namespace
40
3 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
41 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> xValuesData,
4 QVector<double> yValuesData, QVector<double> zValuesData,
42 QVector<double> yValuesData, QVector<double> zValuesData,
5 const Unit &xAxisUnit, const Unit &valuesUnit)
43 const Unit &xAxisUnit, const Unit &valuesUnit)
44 : VectorSeries{std::move(xAxisData), flatten(std::move(xValuesData), std::move(yValuesData),
45 std::move(zValuesData)),
46 xAxisUnit, valuesUnit}
47 {
48 }
49
50 VectorSeries::VectorSeries(QVector<double> xAxisData, QVector<double> valuesData,
51 const Unit &xAxisUnit, const Unit &valuesUnit)
6 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
52 : DataSeries{std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
7 std::make_shared<ArrayData<2> >(QVector<QVector<double> >{
53 std::make_shared<ArrayData<2> >(std::move(valuesData), 3), valuesUnit}
8 std::move(xValuesData), std::move(yValuesData), std::move(zValuesData)}),
9 valuesUnit}
10 {
54 {
11 }
55 }
12
56
13 std::unique_ptr<IDataSeries> VectorSeries::clone() const
57 std::unique_ptr<IDataSeries> VectorSeries::clone() const
14 {
58 {
15 return std::make_unique<VectorSeries>(*this);
59 return std::make_unique<VectorSeries>(*this);
16 }
60 }
17
61
18 std::shared_ptr<IDataSeries> VectorSeries::subDataSeries(const SqpRange &range)
62 std::shared_ptr<IDataSeries> VectorSeries::subDataSeries(const SqpRange &range)
19 {
63 {
20 auto subXAxisData = QVector<double>();
64 auto subXAxisData = QVector<double>();
21 auto subXValuesData = QVector<double>();
65 auto subXValuesData = QVector<double>();
22 auto subYValuesData = QVector<double>();
66 auto subYValuesData = QVector<double>();
23 auto subZValuesData = QVector<double>();
67 auto subZValuesData = QVector<double>();
24
68
25 this->lockRead();
69 this->lockRead();
26 {
70 {
27 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
71 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
28 for (auto it = bounds.first; it != bounds.second; ++it) {
72 for (auto it = bounds.first; it != bounds.second; ++it) {
29 subXAxisData.append(it->x());
73 subXAxisData.append(it->x());
30 subXValuesData.append(it->value(0));
74 subXValuesData.append(it->value(0));
31 subYValuesData.append(it->value(1));
75 subYValuesData.append(it->value(1));
32 subZValuesData.append(it->value(2));
76 subZValuesData.append(it->value(2));
33 }
77 }
34 }
78 }
35 this->unlock();
79 this->unlock();
36
80
37 return std::make_shared<VectorSeries>(subXAxisData, subXValuesData, subYValuesData,
81 return std::make_shared<VectorSeries>(subXAxisData, subXValuesData, subYValuesData,
38 subZValuesData, this->xAxisUnit(), this->valuesUnit());
82 subZValuesData, this->xAxisUnit(), this->valuesUnit());
39 }
83 }
@@ -1,522 +1,519
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 {
14
15 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const QVector<double> &xData,
16 const QVector<double> &valuesData)
17 {
18 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
19 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
20 QVERIFY(std::equal(
21 first, last, valuesData.cbegin(), valuesData.cend(),
22 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
23 }
24
25 } // namespace
26
13 class TestDataSeries : public QObject {
27 class TestDataSeries : public QObject {
14 Q_OBJECT
28 Q_OBJECT
15 private:
29 private:
16 template <typename T>
30 template <typename T>
17 void testValuesBoundsStructure()
31 void testValuesBoundsStructure()
18 {
32 {
19 // ////////////// //
33 // ////////////// //
20 // Test structure //
34 // Test structure //
21 // ////////////// //
35 // ////////////// //
22
36
23 // Data series to get values bounds
37 // Data series to get values bounds
24 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
38 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
25
39
26 // x-axis range
40 // x-axis range
27 QTest::addColumn<double>("minXAxis");
41 QTest::addColumn<double>("minXAxis");
28 QTest::addColumn<double>("maxXAxis");
42 QTest::addColumn<double>("maxXAxis");
29
43
30 // Expected results
44 // Expected results
31 QTest::addColumn<bool>(
45 QTest::addColumn<bool>(
32 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
46 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
33 QTest::addColumn<double>("expectedMinValue");
47 QTest::addColumn<double>("expectedMinValue");
34 QTest::addColumn<double>("expectedMaxValue");
48 QTest::addColumn<double>("expectedMaxValue");
35 }
49 }
36
50
37 template <typename T>
51 template <typename T>
38 void testValuesBounds()
52 void testValuesBounds()
39 {
53 {
40 QFETCH(std::shared_ptr<T>, dataSeries);
54 QFETCH(std::shared_ptr<T>, dataSeries);
41 QFETCH(double, minXAxis);
55 QFETCH(double, minXAxis);
42 QFETCH(double, maxXAxis);
56 QFETCH(double, maxXAxis);
43
57
44 QFETCH(bool, expectedOK);
58 QFETCH(bool, expectedOK);
45 QFETCH(double, expectedMinValue);
59 QFETCH(double, expectedMinValue);
46 QFETCH(double, expectedMaxValue);
60 QFETCH(double, expectedMaxValue);
47
61
48 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
62 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
49 auto end = dataSeries->cend();
63 auto end = dataSeries->cend();
50
64
51 // Checks iterators with expected result
65 // Checks iterators with expected result
52 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
66 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
53
67
54 if (expectedOK) {
68 if (expectedOK) {
55 auto compare = [](const auto &v1, const auto &v2) {
69 auto compare = [](const auto &v1, const auto &v2) {
56 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
70 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
57 };
71 };
58
72
59 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
73 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
60 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
74 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
61 }
75 }
62 }
76 }
63
77
64 private slots:
78 private slots:
65 /// Input test data
79 /// Input test data
66 /// @sa testCtor()
80 /// @sa testCtor()
67 void testCtor_data();
81 void testCtor_data();
68
82
69 /// Tests construction of a data series
83 /// Tests construction of a data series
70 void testCtor();
84 void testCtor();
71
85
72 /// Input test data
86 /// Input test data
73 /// @sa testMerge()
87 /// @sa testMerge()
74 void testMerge_data();
88 void testMerge_data();
75
89
76 /// Tests merge of two data series
90 /// Tests merge of two data series
77 void testMerge();
91 void testMerge();
78
92
79 /// Input test data
93 /// Input test data
80 /// @sa testMinXAxisData()
94 /// @sa testMinXAxisData()
81 void testMinXAxisData_data();
95 void testMinXAxisData_data();
82
96
83 /// Tests get min x-axis data of a data series
97 /// Tests get min x-axis data of a data series
84 void testMinXAxisData();
98 void testMinXAxisData();
85
99
86 /// Input test data
100 /// Input test data
87 /// @sa testMaxXAxisData()
101 /// @sa testMaxXAxisData()
88 void testMaxXAxisData_data();
102 void testMaxXAxisData_data();
89
103
90 /// Tests get max x-axis data of a data series
104 /// Tests get max x-axis data of a data series
91 void testMaxXAxisData();
105 void testMaxXAxisData();
92
106
93 /// Input test data
107 /// Input test data
94 /// @sa testXAxisRange()
108 /// @sa testXAxisRange()
95 void testXAxisRange_data();
109 void testXAxisRange_data();
96
110
97 /// Tests get x-axis range of a data series
111 /// Tests get x-axis range of a data series
98 void testXAxisRange();
112 void testXAxisRange();
99
113
100 /// Input test data
114 /// Input test data
101 /// @sa testValuesBoundsScalar()
115 /// @sa testValuesBoundsScalar()
102 void testValuesBoundsScalar_data();
116 void testValuesBoundsScalar_data();
103
117
104 /// Tests get values bounds of a scalar series
118 /// Tests get values bounds of a scalar series
105 void testValuesBoundsScalar();
119 void testValuesBoundsScalar();
106
120
107 /// Input test data
121 /// Input test data
108 /// @sa testValuesBoundsVector()
122 /// @sa testValuesBoundsVector()
109 void testValuesBoundsVector_data();
123 void testValuesBoundsVector_data();
110
124
111 /// Tests get values bounds of a vector series
125 /// Tests get values bounds of a vector series
112 void testValuesBoundsVector();
126 void testValuesBoundsVector();
113 };
127 };
114
128
115 void TestDataSeries::testCtor_data()
129 void TestDataSeries::testCtor_data()
116 {
130 {
117 // ////////////// //
131 // ////////////// //
118 // Test structure //
132 // Test structure //
119 // ////////////// //
133 // ////////////// //
120
134
121 // x-axis data
135 // x-axis data
122 QTest::addColumn<QVector<double> >("xAxisData");
136 QTest::addColumn<QVector<double> >("xAxisData");
123 // values data
137 // values data
124 QTest::addColumn<QVector<double> >("valuesData");
138 QTest::addColumn<QVector<double> >("valuesData");
125
139
126 // expected x-axis data
140 // expected x-axis data
127 QTest::addColumn<QVector<double> >("expectedXAxisData");
141 QTest::addColumn<QVector<double> >("expectedXAxisData");
128 // expected values data
142 // expected values data
129 QTest::addColumn<QVector<double> >("expectedValuesData");
143 QTest::addColumn<QVector<double> >("expectedValuesData");
130
144
131 // ////////// //
145 // ////////// //
132 // Test cases //
146 // Test cases //
133 // ////////// //
147 // ////////// //
134
148
135 QTest::newRow("invalidData (different sizes of vectors)")
149 QTest::newRow("invalidData (different sizes of vectors)")
136 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
150 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
137 << QVector<double>{} << QVector<double>{};
151 << QVector<double>{} << QVector<double>{};
138
152
139 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
153 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
140 << QVector<double>{100., 200., 300., 400., 500.}
154 << QVector<double>{100., 200., 300., 400., 500.}
141 << QVector<double>{1., 2., 3., 4., 5.}
155 << QVector<double>{1., 2., 3., 4., 5.}
142 << QVector<double>{100., 200., 300., 400., 500.};
156 << QVector<double>{100., 200., 300., 400., 500.};
143
157
144 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
158 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
145 << QVector<double>{100., 200., 300., 400., 500.}
159 << QVector<double>{100., 200., 300., 400., 500.}
146 << QVector<double>{1., 2., 3., 4., 5.}
160 << QVector<double>{1., 2., 3., 4., 5.}
147 << QVector<double>{500., 400., 300., 200., 100.};
161 << QVector<double>{500., 400., 300., 200., 100.};
148
162
149 QTest::newRow("unsortedData2")
163 QTest::newRow("unsortedData2")
150 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
164 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
151 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
165 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
152 }
166 }
153
167
154 void TestDataSeries::testCtor()
168 void TestDataSeries::testCtor()
155 {
169 {
156 // Creates series
170 // Creates series
157 QFETCH(QVector<double>, xAxisData);
171 QFETCH(QVector<double>, xAxisData);
158 QFETCH(QVector<double>, valuesData);
172 QFETCH(QVector<double>, valuesData);
159
173
160 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
174 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
161 Unit{}, Unit{});
175 Unit{}, Unit{});
162
176
163 // Validates results : we check that the data series is sorted on its x-axis data
177 // Validates results : we check that the data series is sorted on its x-axis data
164 QFETCH(QVector<double>, expectedXAxisData);
178 QFETCH(QVector<double>, expectedXAxisData);
165 QFETCH(QVector<double>, expectedValuesData);
179 QFETCH(QVector<double>, expectedValuesData);
166
180
167 auto seriesXAxisData = series->xAxisData()->data();
181 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
168 auto seriesValuesData = series->valuesData()->data();
169
170 QVERIFY(
171 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
172 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
173 seriesValuesData.cbegin()));
174 }
182 }
175
183
176 namespace {
184 namespace {
177
185
178 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
186 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
179 QVector<double> valuesData)
187 QVector<double> valuesData)
180 {
188 {
181 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
189 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
182 Unit{});
190 Unit{});
183 }
191 }
184
192
185 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
193 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
186 QVector<double> xValuesData,
194 QVector<double> xValuesData,
187 QVector<double> yValuesData,
195 QVector<double> yValuesData,
188 QVector<double> zValuesData)
196 QVector<double> zValuesData)
189 {
197 {
190 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
198 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
191 std::move(yValuesData), std::move(zValuesData), Unit{},
199 std::move(yValuesData), std::move(zValuesData), Unit{},
192 Unit{});
200 Unit{});
193 }
201 }
194
202
195 } // namespace
203 } // namespace
196
204
197 void TestDataSeries::testMerge_data()
205 void TestDataSeries::testMerge_data()
198 {
206 {
199 // ////////////// //
207 // ////////////// //
200 // Test structure //
208 // Test structure //
201 // ////////////// //
209 // ////////////// //
202
210
203 // Data series to merge
211 // Data series to merge
204 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
212 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
205 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
213 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
206
214
207 // Expected values in the first data series after merge
215 // Expected values in the first data series after merge
208 QTest::addColumn<QVector<double> >("expectedXAxisData");
216 QTest::addColumn<QVector<double> >("expectedXAxisData");
209 QTest::addColumn<QVector<double> >("expectedValuesData");
217 QTest::addColumn<QVector<double> >("expectedValuesData");
210
218
211 // ////////// //
219 // ////////// //
212 // Test cases //
220 // Test cases //
213 // ////////// //
221 // ////////// //
214
222
215 QTest::newRow("sortedMerge")
223 QTest::newRow("sortedMerge")
216 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
224 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
217 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
225 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
218 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
226 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
219 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
227 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
220
228
221 QTest::newRow("unsortedMerge")
229 QTest::newRow("unsortedMerge")
222 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
230 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
223 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
231 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
224 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
232 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
225 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
233 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
226
234
227 QTest::newRow("unsortedMerge2")
235 QTest::newRow("unsortedMerge2")
228 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
236 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
229 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
237 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
230 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
238 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
231 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
239 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
232
240
233 QTest::newRow("unsortedMerge3")
241 QTest::newRow("unsortedMerge3")
234 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
242 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
235 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
243 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
236 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
244 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
237 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
245 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
238 }
246 }
239
247
240 void TestDataSeries::testMerge()
248 void TestDataSeries::testMerge()
241 {
249 {
242 // Merges series
250 // Merges series
243 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
251 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
244 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
252 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
245
253
246 dataSeries->merge(dataSeries2.get());
254 dataSeries->merge(dataSeries2.get());
247
255
248 // Validates results : we check that the merge is valid and the data series is sorted on its
256 // Validates results : we check that the merge is valid and the data series is sorted on its
249 // x-axis data
257 // x-axis data
250 QFETCH(QVector<double>, expectedXAxisData);
258 QFETCH(QVector<double>, expectedXAxisData);
251 QFETCH(QVector<double>, expectedValuesData);
259 QFETCH(QVector<double>, expectedValuesData);
252
260
253 auto seriesXAxisData = dataSeries->xAxisData()->data();
261 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
254 auto seriesValuesData = dataSeries->valuesData()->data();
255
256 QVERIFY(
257 std::equal(expectedXAxisData.cbegin(), expectedXAxisData.cend(), seriesXAxisData.cbegin()));
258 QVERIFY(std::equal(expectedValuesData.cbegin(), expectedValuesData.cend(),
259 seriesValuesData.cbegin()));
260 }
262 }
261
263
262 void TestDataSeries::testMinXAxisData_data()
264 void TestDataSeries::testMinXAxisData_data()
263 {
265 {
264 // ////////////// //
266 // ////////////// //
265 // Test structure //
267 // Test structure //
266 // ////////////// //
268 // ////////////// //
267
269
268 // Data series to get min data
270 // Data series to get min data
269 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
271 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
270
272
271 // Min data
273 // Min data
272 QTest::addColumn<double>("min");
274 QTest::addColumn<double>("min");
273
275
274 // Expected results
276 // Expected results
275 QTest::addColumn<bool>(
277 QTest::addColumn<bool>(
276 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
278 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
277 QTest::addColumn<double>(
279 QTest::addColumn<double>(
278 "expectedMin"); // Expected value when method doesn't return end iterator
280 "expectedMin"); // Expected value when method doesn't return end iterator
279
281
280 // ////////// //
282 // ////////// //
281 // Test cases //
283 // Test cases //
282 // ////////// //
284 // ////////// //
283
285
284 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
286 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
285 {100., 200., 300., 400., 500.})
287 {100., 200., 300., 400., 500.})
286 << 0. << true << 1.;
288 << 0. << true << 1.;
287 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
289 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
288 {100., 200., 300., 400., 500.})
290 {100., 200., 300., 400., 500.})
289 << 1. << true << 1.;
291 << 1. << true << 1.;
290 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
292 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
291 {100., 200., 300., 400., 500.})
293 {100., 200., 300., 400., 500.})
292 << 1.1 << true << 2.;
294 << 1.1 << true << 2.;
293 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
295 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
294 {100., 200., 300., 400., 500.})
296 {100., 200., 300., 400., 500.})
295 << 5. << true << 5.;
297 << 5. << true << 5.;
296 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
298 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
297 {100., 200., 300., 400., 500.})
299 {100., 200., 300., 400., 500.})
298 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
300 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
299 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
301 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
300 << std::numeric_limits<double>::quiet_NaN();
302 << std::numeric_limits<double>::quiet_NaN();
301 }
303 }
302
304
303 void TestDataSeries::testMinXAxisData()
305 void TestDataSeries::testMinXAxisData()
304 {
306 {
305 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
307 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
306 QFETCH(double, min);
308 QFETCH(double, min);
307
309
308 QFETCH(bool, expectedOK);
310 QFETCH(bool, expectedOK);
309 QFETCH(double, expectedMin);
311 QFETCH(double, expectedMin);
310
312
311 auto it = dataSeries->minXAxisData(min);
313 auto it = dataSeries->minXAxisData(min);
312
314
313 QCOMPARE(expectedOK, it != dataSeries->cend());
315 QCOMPARE(expectedOK, it != dataSeries->cend());
314
316
315 // If the method doesn't return a end iterator, checks with expected value
317 // If the method doesn't return a end iterator, checks with expected value
316 if (expectedOK) {
318 if (expectedOK) {
317 QCOMPARE(expectedMin, it->x());
319 QCOMPARE(expectedMin, it->x());
318 }
320 }
319 }
321 }
320
322
321 void TestDataSeries::testMaxXAxisData_data()
323 void TestDataSeries::testMaxXAxisData_data()
322 {
324 {
323 // ////////////// //
325 // ////////////// //
324 // Test structure //
326 // Test structure //
325 // ////////////// //
327 // ////////////// //
326
328
327 // Data series to get max data
329 // Data series to get max data
328 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
330 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
329
331
330 // Max data
332 // Max data
331 QTest::addColumn<double>("max");
333 QTest::addColumn<double>("max");
332
334
333 // Expected results
335 // Expected results
334 QTest::addColumn<bool>(
336 QTest::addColumn<bool>(
335 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
337 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
336 QTest::addColumn<double>(
338 QTest::addColumn<double>(
337 "expectedMax"); // Expected value when method doesn't return end iterator
339 "expectedMax"); // Expected value when method doesn't return end iterator
338
340
339 // ////////// //
341 // ////////// //
340 // Test cases //
342 // Test cases //
341 // ////////// //
343 // ////////// //
342
344
343 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
345 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
344 {100., 200., 300., 400., 500.})
346 {100., 200., 300., 400., 500.})
345 << 6. << true << 5.;
347 << 6. << true << 5.;
346 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
348 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
347 {100., 200., 300., 400., 500.})
349 {100., 200., 300., 400., 500.})
348 << 5. << true << 5.;
350 << 5. << true << 5.;
349 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
351 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
350 {100., 200., 300., 400., 500.})
352 {100., 200., 300., 400., 500.})
351 << 4.9 << true << 4.;
353 << 4.9 << true << 4.;
352 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
354 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
353 {100., 200., 300., 400., 500.})
355 {100., 200., 300., 400., 500.})
354 << 1.1 << true << 1.;
356 << 1.1 << true << 1.;
355 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
357 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
356 {100., 200., 300., 400., 500.})
358 {100., 200., 300., 400., 500.})
357 << 1. << true << 1.;
359 << 1. << true << 1.;
358 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
360 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
359 << std::numeric_limits<double>::quiet_NaN();
361 << std::numeric_limits<double>::quiet_NaN();
360 }
362 }
361
363
362 void TestDataSeries::testMaxXAxisData()
364 void TestDataSeries::testMaxXAxisData()
363 {
365 {
364 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
366 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
365 QFETCH(double, max);
367 QFETCH(double, max);
366
368
367 QFETCH(bool, expectedOK);
369 QFETCH(bool, expectedOK);
368 QFETCH(double, expectedMax);
370 QFETCH(double, expectedMax);
369
371
370 auto it = dataSeries->maxXAxisData(max);
372 auto it = dataSeries->maxXAxisData(max);
371
373
372 QCOMPARE(expectedOK, it != dataSeries->cend());
374 QCOMPARE(expectedOK, it != dataSeries->cend());
373
375
374 // If the method doesn't return a end iterator, checks with expected value
376 // If the method doesn't return a end iterator, checks with expected value
375 if (expectedOK) {
377 if (expectedOK) {
376 QCOMPARE(expectedMax, it->x());
378 QCOMPARE(expectedMax, it->x());
377 }
379 }
378 }
380 }
379
381
380 void TestDataSeries::testXAxisRange_data()
382 void TestDataSeries::testXAxisRange_data()
381 {
383 {
382 // ////////////// //
384 // ////////////// //
383 // Test structure //
385 // Test structure //
384 // ////////////// //
386 // ////////////// //
385
387
386 // Data series to get x-axis range
388 // Data series to get x-axis range
387 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
389 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
388
390
389 // Min/max values
391 // Min/max values
390 QTest::addColumn<double>("min");
392 QTest::addColumn<double>("min");
391 QTest::addColumn<double>("max");
393 QTest::addColumn<double>("max");
392
394
393 // Expected values
395 // Expected values
394 QTest::addColumn<QVector<double> >("expectedXAxisData");
396 QTest::addColumn<QVector<double> >("expectedXAxisData");
395 QTest::addColumn<QVector<double> >("expectedValuesData");
397 QTest::addColumn<QVector<double> >("expectedValuesData");
396
398
397 // ////////// //
399 // ////////// //
398 // Test cases //
400 // Test cases //
399 // ////////// //
401 // ////////// //
400
402
401 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
403 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
402 {100., 200., 300., 400., 500.})
404 {100., 200., 300., 400., 500.})
403 << -1. << 3.2 << QVector<double>{1., 2., 3.}
405 << -1. << 3.2 << QVector<double>{1., 2., 3.}
404 << QVector<double>{100., 200., 300.};
406 << QVector<double>{100., 200., 300.};
405 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
407 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
406 {100., 200., 300., 400., 500.})
408 {100., 200., 300., 400., 500.})
407 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
409 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
408 << QVector<double>{100., 200., 300., 400.};
410 << QVector<double>{100., 200., 300., 400.};
409 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
411 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
410 {100., 200., 300., 400., 500.})
412 {100., 200., 300., 400., 500.})
411 << 1. << 3.9 << QVector<double>{1., 2., 3.}
413 << 1. << 3.9 << QVector<double>{1., 2., 3.}
412 << QVector<double>{100., 200., 300.};
414 << QVector<double>{100., 200., 300.};
413 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
415 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
414 {100., 200., 300., 400., 500.})
416 {100., 200., 300., 400., 500.})
415 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
417 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
416 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
418 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
417 {100., 200., 300., 400., 500.})
419 {100., 200., 300., 400., 500.})
418 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
420 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
419 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
421 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
420 {100., 200., 300., 400., 500.})
422 {100., 200., 300., 400., 500.})
421 << 2.1 << 6. << QVector<double>{3., 4., 5.}
423 << 2.1 << 6. << QVector<double>{3., 4., 5.}
422 << QVector<double>{300., 400., 500.};
424 << QVector<double>{300., 400., 500.};
423 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
425 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
424 {100., 200., 300., 400., 500.})
426 {100., 200., 300., 400., 500.})
425 << 6. << 9. << QVector<double>{} << QVector<double>{};
427 << 6. << 9. << QVector<double>{} << QVector<double>{};
426 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
428 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
427 {100., 200., 300., 400., 500.})
429 {100., 200., 300., 400., 500.})
428 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
430 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
429 }
431 }
430
432
431 void TestDataSeries::testXAxisRange()
433 void TestDataSeries::testXAxisRange()
432 {
434 {
433 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
435 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
434 QFETCH(double, min);
436 QFETCH(double, min);
435 QFETCH(double, max);
437 QFETCH(double, max);
436
438
437 QFETCH(QVector<double>, expectedXAxisData);
439 QFETCH(QVector<double>, expectedXAxisData);
438 QFETCH(QVector<double>, expectedValuesData);
440 QFETCH(QVector<double>, expectedValuesData);
439
441
440 auto bounds = dataSeries->xAxisRange(min, max);
442 auto bounds = dataSeries->xAxisRange(min, max);
441 QVERIFY(std::equal(bounds.first, bounds.second, expectedXAxisData.cbegin(),
443 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
442 expectedXAxisData.cend(),
443 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
444 QVERIFY(std::equal(
445 bounds.first, bounds.second, expectedValuesData.cbegin(), expectedValuesData.cend(),
446 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
447 }
444 }
448
445
449 void TestDataSeries::testValuesBoundsScalar_data()
446 void TestDataSeries::testValuesBoundsScalar_data()
450 {
447 {
451 testValuesBoundsStructure<ScalarSeries>();
448 testValuesBoundsStructure<ScalarSeries>();
452
449
453 // ////////// //
450 // ////////// //
454 // Test cases //
451 // Test cases //
455 // ////////// //
452 // ////////// //
456 auto nan = std::numeric_limits<double>::quiet_NaN();
453 auto nan = std::numeric_limits<double>::quiet_NaN();
457
454
458 QTest::newRow("scalarBounds1")
455 QTest::newRow("scalarBounds1")
459 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
456 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
460 << true << 100. << 500.;
457 << true << 100. << 500.;
461 QTest::newRow("scalarBounds2")
458 QTest::newRow("scalarBounds2")
462 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
459 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
463 << true << 200. << 400.;
460 << true << 200. << 400.;
464 QTest::newRow("scalarBounds3")
461 QTest::newRow("scalarBounds3")
465 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
462 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
466 << false << nan << nan;
463 << false << nan << nan;
467 QTest::newRow("scalarBounds4")
464 QTest::newRow("scalarBounds4")
468 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
465 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
469 << false << nan << nan;
466 << false << nan << nan;
470 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
467 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
471 << 100.;
468 << 100.;
472 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
469 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
473
470
474 // Tests with NaN values: NaN values are not included in min/max search
471 // Tests with NaN values: NaN values are not included in min/max search
475 QTest::newRow("scalarBounds7")
472 QTest::newRow("scalarBounds7")
476 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
473 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
477 << true << 200. << 400.;
474 << true << 200. << 400.;
478 QTest::newRow("scalarBounds8")
475 QTest::newRow("scalarBounds8")
479 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
476 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
480 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
477 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
481 }
478 }
482
479
483 void TestDataSeries::testValuesBoundsScalar()
480 void TestDataSeries::testValuesBoundsScalar()
484 {
481 {
485 testValuesBounds<ScalarSeries>();
482 testValuesBounds<ScalarSeries>();
486 }
483 }
487
484
488 void TestDataSeries::testValuesBoundsVector_data()
485 void TestDataSeries::testValuesBoundsVector_data()
489 {
486 {
490 testValuesBoundsStructure<VectorSeries>();
487 testValuesBoundsStructure<VectorSeries>();
491
488
492 // ////////// //
489 // ////////// //
493 // Test cases //
490 // Test cases //
494 // ////////// //
491 // ////////// //
495 auto nan = std::numeric_limits<double>::quiet_NaN();
492 auto nan = std::numeric_limits<double>::quiet_NaN();
496
493
497 QTest::newRow("vectorBounds1")
494 QTest::newRow("vectorBounds1")
498 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
495 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
499 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
496 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
500 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
497 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
501 QTest::newRow("vectorBounds2")
498 QTest::newRow("vectorBounds2")
502 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
499 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
503 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
500 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
504 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
501 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
505 QTest::newRow("vectorBounds3")
502 QTest::newRow("vectorBounds3")
506 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
503 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
507 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
504 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
508 << 2. << 3. << true << 10. << 24.;
505 << 2. << 3. << true << 10. << 24.;
509
506
510 // Tests with NaN values: NaN values are not included in min/max search
507 // Tests with NaN values: NaN values are not included in min/max search
511 QTest::newRow("vectorBounds4")
508 QTest::newRow("vectorBounds4")
512 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
509 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
513 << nan << nan;
510 << nan << nan;
514 }
511 }
515
512
516 void TestDataSeries::testValuesBoundsVector()
513 void TestDataSeries::testValuesBoundsVector()
517 {
514 {
518 testValuesBounds<VectorSeries>();
515 testValuesBounds<VectorSeries>();
519 }
516 }
520
517
521 QTEST_MAIN(TestDataSeries)
518 QTEST_MAIN(TestDataSeries)
522 #include "TestDataSeries.moc"
519 #include "TestDataSeries.moc"
@@ -1,199 +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 {
6
7 void verifyArrayData(const ArrayData<1> &arrayData, const QVector<double> &expectedData)
8 {
9 QVERIFY(std::equal(
10 arrayData.cbegin(), arrayData.cend(), expectedData.cbegin(), expectedData.cend(),
11 [](const auto &it, const auto &expectedData) { return it.at(0) == expectedData; }));
12 }
13
14 } // namespace
15
5 class TestOneDimArrayData : public QObject {
16 class TestOneDimArrayData : public QObject {
6 Q_OBJECT
17 Q_OBJECT
7 private slots:
18 private slots:
8 /// Tests @sa ArrayData::data()
19 /// Tests @sa ArrayData::data()
9 void testData_data();
20 void testData_data();
10 void testData();
21 void testData();
11
22
12 /// Tests @sa ArrayData::data(int componentIndex)
13 void testDataByComponentIndex_data();
14 void testDataByComponentIndex();
15
16 /// Tests @sa ArrayData::add()
23 /// Tests @sa ArrayData::add()
17 void testAdd_data();
24 void testAdd_data();
18 void testAdd();
25 void testAdd();
19
26
20 /// Tests @sa ArrayData::at(int index)
27 /// Tests @sa ArrayData::at(int index)
21 void testAt_data();
28 void testAt_data();
22 void testAt();
29 void testAt();
23
30
24 /// Tests @sa ArrayData::clear()
31 /// Tests @sa ArrayData::clear()
25 void testClear_data();
32 void testClear_data();
26 void testClear();
33 void testClear();
27
34
28 /// Tests @sa ArrayData::size()
35 /// Tests @sa ArrayData::size()
29 void testSize_data();
36 void testSize_data();
30 void testSize();
37 void testSize();
31
38
32 /// Tests @sa ArrayData::sort()
39 /// Tests @sa ArrayData::sort()
33 void testSort_data();
40 void testSort_data();
34 void testSort();
41 void testSort();
35 };
42 };
36
43
37 void TestOneDimArrayData::testData_data()
44 void TestOneDimArrayData::testData_data()
38 {
45 {
39 // Test structure
46 // Test structure
40 QTest::addColumn<QVector<double> >("inputData"); // array's data input
47 QTest::addColumn<QVector<double> >("inputData"); // array's data input
41 QTest::addColumn<QVector<double> >("expectedData"); // expected data
48 QTest::addColumn<QVector<double> >("expectedData"); // expected data
42
49
43 // Test cases
50 // Test cases
44 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.}
51 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.}
45 << QVector<double>{1., 2., 3., 4., 5.};
52 << QVector<double>{1., 2., 3., 4., 5.};
46 }
53 }
47
54
48 void TestOneDimArrayData::testData()
55 void TestOneDimArrayData::testData()
49 {
56 {
50 QFETCH(QVector<double>, inputData);
57 QFETCH(QVector<double>, inputData);
51 QFETCH(QVector<double>, expectedData);
58 QFETCH(QVector<double>, expectedData);
52
59
53 ArrayData<1> arrayData{inputData};
60 ArrayData<1> arrayData{inputData};
54 QVERIFY(arrayData.data() == expectedData);
61 verifyArrayData(arrayData, expectedData);
55 }
56
57 void TestOneDimArrayData::testDataByComponentIndex_data()
58 {
59 // Test structure
60 QTest::addColumn<QVector<double> >("inputData"); // array data's input
61 QTest::addColumn<int>("componentIndex"); // component index to test
62 QTest::addColumn<QVector<double> >("expectedData"); // expected data
63
64 // Test cases
65 QTest::newRow("validIndex") << QVector<double>{1., 2., 3., 4., 5.} << 0
66 << QVector<double>{1., 2., 3., 4., 5.};
67 QTest::newRow("invalidIndex1") << QVector<double>{1., 2., 3., 4., 5.} << -1
68 << QVector<double>{};
69 QTest::newRow("invalidIndex2") << QVector<double>{1., 2., 3., 4., 5.} << 1 << QVector<double>{};
70 }
71
72 void TestOneDimArrayData::testDataByComponentIndex()
73 {
74 QFETCH(QVector<double>, inputData);
75 QFETCH(int, componentIndex);
76 QFETCH(QVector<double>, expectedData);
77
78 ArrayData<1> arrayData{inputData};
79 QVERIFY(arrayData.data(componentIndex) == expectedData);
80 }
62 }
81
63
82 void TestOneDimArrayData::testAdd_data()
64 void TestOneDimArrayData::testAdd_data()
83 {
65 {
84 // Test structure
66 // Test structure
85 QTest::addColumn<QVector<double> >("inputData"); // array's data input
67 QTest::addColumn<QVector<double> >("inputData"); // array's data input
86 QTest::addColumn<QVector<double> >("otherData"); // array data's input to merge with
68 QTest::addColumn<QVector<double> >("otherData"); // array data's input to merge with
87 QTest::addColumn<bool>("prepend"); // prepend or append merge
69 QTest::addColumn<bool>("prepend"); // prepend or append merge
88 QTest::addColumn<QVector<double> >("expectedData"); // expected data after merge
70 QTest::addColumn<QVector<double> >("expectedData"); // expected data after merge
89
71
90 // Test cases
72 // Test cases
91 QTest::newRow("appendMerge") << QVector<double>{1., 2., 3., 4., 5.}
73 QTest::newRow("appendMerge") << QVector<double>{1., 2., 3., 4., 5.}
92 << QVector<double>{6., 7., 8.} << false
74 << QVector<double>{6., 7., 8.} << false
93 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8.};
75 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8.};
94 QTest::newRow("prependMerge") << QVector<double>{1., 2., 3., 4., 5.}
76 QTest::newRow("prependMerge") << QVector<double>{1., 2., 3., 4., 5.}
95 << QVector<double>{6., 7., 8.} << true
77 << QVector<double>{6., 7., 8.} << true
96 << QVector<double>{6., 7., 8., 1., 2., 3., 4., 5.};
78 << QVector<double>{6., 7., 8., 1., 2., 3., 4., 5.};
97 }
79 }
98
80
99 void TestOneDimArrayData::testAdd()
81 void TestOneDimArrayData::testAdd()
100 {
82 {
101 QFETCH(QVector<double>, inputData);
83 QFETCH(QVector<double>, inputData);
102 QFETCH(QVector<double>, otherData);
84 QFETCH(QVector<double>, otherData);
103 QFETCH(bool, prepend);
85 QFETCH(bool, prepend);
104 QFETCH(QVector<double>, expectedData);
86 QFETCH(QVector<double>, expectedData);
105
87
106 ArrayData<1> arrayData{inputData};
88 ArrayData<1> arrayData{inputData};
107 ArrayData<1> other{otherData};
89 ArrayData<1> other{otherData};
108
90
109 arrayData.add(other, prepend);
91 arrayData.add(other, prepend);
110 QVERIFY(arrayData.data() == expectedData);
92 verifyArrayData(arrayData, expectedData);
111 }
93 }
112
94
113 void TestOneDimArrayData::testAt_data()
95 void TestOneDimArrayData::testAt_data()
114 {
96 {
115 // Test structure
97 // Test structure
116 QTest::addColumn<QVector<double> >("inputData"); // array data's input
98 QTest::addColumn<QVector<double> >("inputData"); // array data's input
117 QTest::addColumn<int>("index"); // index to retrieve data
99 QTest::addColumn<int>("index"); // index to retrieve data
118 QTest::addColumn<double>("expectedData"); // expected data at index
100 QTest::addColumn<double>("expectedData"); // expected data at index
119
101
120 // Test cases
102 // Test cases
121 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << 0 << 1.;
103 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << 0 << 1.;
122 QTest::newRow("data2") << QVector<double>{1., 2., 3., 4., 5.} << 3 << 4.;
104 QTest::newRow("data2") << QVector<double>{1., 2., 3., 4., 5.} << 3 << 4.;
123 }
105 }
124
106
125 void TestOneDimArrayData::testAt()
107 void TestOneDimArrayData::testAt()
126 {
108 {
127 QFETCH(QVector<double>, inputData);
109 QFETCH(QVector<double>, inputData);
128 QFETCH(int, index);
110 QFETCH(int, index);
129 QFETCH(double, expectedData);
111 QFETCH(double, expectedData);
130
112
131 ArrayData<1> arrayData{inputData};
113 ArrayData<1> arrayData{inputData};
132 QVERIFY(arrayData.at(index) == expectedData);
114 QVERIFY(arrayData.at(index) == expectedData);
133 }
115 }
134
116
135 void TestOneDimArrayData::testClear_data()
117 void TestOneDimArrayData::testClear_data()
136 {
118 {
137 // Test structure
119 // Test structure
138 QTest::addColumn<QVector<double> >("inputData"); // array data's input
120 QTest::addColumn<QVector<double> >("inputData"); // array data's input
139
121
140 // Test cases
122 // Test cases
141 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.};
123 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.};
142 }
124 }
143
125
144 void TestOneDimArrayData::testClear()
126 void TestOneDimArrayData::testClear()
145 {
127 {
146 QFETCH(QVector<double>, inputData);
128 QFETCH(QVector<double>, inputData);
147
129
148 ArrayData<1> arrayData{inputData};
130 ArrayData<1> arrayData{inputData};
149 arrayData.clear();
131 arrayData.clear();
150 QVERIFY(arrayData.data() == QVector<double>{});
132 verifyArrayData(arrayData, QVector<double>{});
151 }
133 }
152
134
153 void TestOneDimArrayData::testSize_data()
135 void TestOneDimArrayData::testSize_data()
154 {
136 {
155 // Test structure
137 // Test structure
156 QTest::addColumn<QVector<double> >("inputData"); // array data's input
138 QTest::addColumn<QVector<double> >("inputData"); // array data's input
157 QTest::addColumn<int>("expectedSize"); // expected array data size
139 QTest::addColumn<int>("expectedSize"); // expected array data size
158
140
159 // Test cases
141 // Test cases
160 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << 5;
142 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << 5;
161 }
143 }
162
144
163 void TestOneDimArrayData::testSize()
145 void TestOneDimArrayData::testSize()
164 {
146 {
165 QFETCH(QVector<double>, inputData);
147 QFETCH(QVector<double>, inputData);
166 QFETCH(int, expectedSize);
148 QFETCH(int, expectedSize);
167
149
168 ArrayData<1> arrayData{inputData};
150 ArrayData<1> arrayData{inputData};
169 QVERIFY(arrayData.size() == expectedSize);
151 QVERIFY(arrayData.size() == expectedSize);
170 }
152 }
171
153
172 void TestOneDimArrayData::testSort_data()
154 void TestOneDimArrayData::testSort_data()
173 {
155 {
174 // Test structure
156 // Test structure
175 QTest::addColumn<QVector<double> >("inputData"); // array data's input
157 QTest::addColumn<QVector<double> >("inputData"); // array data's input
176 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
158 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
177 QTest::addColumn<QVector<double> >("expectedData"); // expected data after sorting
159 QTest::addColumn<QVector<double> >("expectedData"); // expected data after sorting
178
160
179 // Test cases
161 // Test cases
180 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << std::vector<int>{0, 2, 3, 1, 4}
162 QTest::newRow("data1") << QVector<double>{1., 2., 3., 4., 5.} << std::vector<int>{0, 2, 3, 1, 4}
181 << QVector<double>{1., 3., 4., 2., 5.};
163 << QVector<double>{1., 3., 4., 2., 5.};
182 QTest::newRow("data2") << QVector<double>{1., 2., 3., 4., 5.} << std::vector<int>{4, 1, 2, 3, 0}
164 QTest::newRow("data2") << QVector<double>{1., 2., 3., 4., 5.} << std::vector<int>{4, 1, 2, 3, 0}
183 << QVector<double>{5., 2., 3., 4., 1.};
165 << QVector<double>{5., 2., 3., 4., 1.};
184 }
166 }
185
167
186 void TestOneDimArrayData::testSort()
168 void TestOneDimArrayData::testSort()
187 {
169 {
188 QFETCH(QVector<double>, inputData);
170 QFETCH(QVector<double>, inputData);
189 QFETCH(std::vector<int>, sortPermutation);
171 QFETCH(std::vector<int>, sortPermutation);
190 QFETCH(QVector<double>, expectedData);
172 QFETCH(QVector<double>, expectedData);
191
173
192 ArrayData<1> arrayData{inputData};
174 ArrayData<1> arrayData{inputData};
193 auto sortedArrayData = arrayData.sort(sortPermutation);
175 auto sortedArrayData = arrayData.sort(sortPermutation);
194 QVERIFY(sortedArrayData != nullptr);
176 QVERIFY(sortedArrayData != nullptr);
195 QVERIFY(sortedArrayData->data() == expectedData);
177 verifyArrayData(*sortedArrayData, expectedData);
196 }
178 }
197
179
198 QTEST_MAIN(TestOneDimArrayData)
180 QTEST_MAIN(TestOneDimArrayData)
199 #include "TestOneDimArrayData.moc"
181 #include "TestOneDimArrayData.moc"
@@ -1,224 +1,239
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 DataContainer = QVector<QVector<double> >;
5 using Container = QVector<QVector<double> >;
6 using InputData = QPair<QVector<double>, int>;
7
8 namespace {
9
10 InputData flatten(const Container &container)
11 {
12 if (container.isEmpty()) {
13 return {};
14 }
15
16 // We assume here that each component of the container have the same size
17 auto containerSize = container.size();
18 auto componentSize = container.first().size();
19
20 auto result = QVector<double>{};
21 result.reserve(componentSize * containerSize);
22
23 for (auto i = 0; i < componentSize; ++i) {
24 for (auto j = 0; j < containerSize; ++j) {
25 result.append(container.at(j).at(i));
26 }
27 }
28
29 return {result, containerSize};
30 }
31
32 void verifyArrayData(const ArrayData<2> &arrayData, const Container &expectedData)
33 {
34 auto verifyComponent = [&arrayData](const auto &componentData, const auto &equalFun) {
35 QVERIFY(std::equal(arrayData.cbegin(), arrayData.cend(), componentData.cbegin(),
36 componentData.cend(),
37 [&equalFun](const auto &dataSeriesIt, const auto &expectedValue) {
38 return equalFun(dataSeriesIt, expectedValue);
39 }));
40 };
41
42 for (auto i = 0; i < expectedData.size(); ++i) {
43 verifyComponent(expectedData.at(i), [i](const auto &seriesIt, const auto &value) {
44 return seriesIt.at(i) == value;
45 });
46 }
47 }
48
49 } // namespace
6
50
7 class TestTwoDimArrayData : public QObject {
51 class TestTwoDimArrayData : public QObject {
8 Q_OBJECT
52 Q_OBJECT
9 private slots:
53 private slots:
10 /// Tests @sa ArrayData::data(int componentIndex)
11 void testDataByComponentIndex_data();
12 void testDataByComponentIndex();
13
14 /// Tests @sa ArrayData ctor
54 /// Tests @sa ArrayData ctor
15 void testCtor_data();
55 void testCtor_data();
16 void testCtor();
56 void testCtor();
17
57
18 /// Tests @sa ArrayData::add()
58 /// Tests @sa ArrayData::add()
19 void testAdd_data();
59 void testAdd_data();
20 void testAdd();
60 void testAdd();
21
61
22 /// Tests @sa ArrayData::clear()
62 /// Tests @sa ArrayData::clear()
23 void testClear_data();
63 void testClear_data();
24 void testClear();
64 void testClear();
25
65
26 /// Tests @sa ArrayData::size()
66 /// Tests @sa ArrayData::size()
27 void testSize_data();
67 void testSize_data();
28 void testSize();
68 void testSize();
29
69
30 /// Tests @sa ArrayData::sort()
70 /// Tests @sa ArrayData::sort()
31 void testSort_data();
71 void testSort_data();
32 void testSort();
72 void testSort();
33 };
73 };
34
74
35 void TestTwoDimArrayData::testDataByComponentIndex_data()
36 {
37 // Test structure
38 QTest::addColumn<DataContainer>("inputData"); // array data's input
39 QTest::addColumn<int>("componentIndex"); // component index to test
40 QTest::addColumn<QVector<double> >("expectedData"); // expected data
41
42 // Test cases
43 auto inputData
44 = DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
45
46 QTest::newRow("validIndex1") << inputData << 0 << QVector<double>{1., 2., 3., 4., 5.};
47 QTest::newRow("validIndex2") << inputData << 1 << QVector<double>{6., 7., 8., 9., 10.};
48 QTest::newRow("validIndex3") << inputData << 2 << QVector<double>{11., 12., 13., 14., 15.};
49 QTest::newRow("invalidIndex1") << inputData << -1 << QVector<double>{};
50 QTest::newRow("invalidIndex2") << inputData << 3 << QVector<double>{};
51 }
52
53 void TestTwoDimArrayData::testDataByComponentIndex()
54 {
55 QFETCH(DataContainer, inputData);
56 QFETCH(int, componentIndex);
57 QFETCH(QVector<double>, expectedData);
58
59 ArrayData<2> arrayData{inputData};
60 QVERIFY(arrayData.data(componentIndex) == expectedData);
61 }
62
63 void TestTwoDimArrayData::testCtor_data()
75 void TestTwoDimArrayData::testCtor_data()
64 {
76 {
65 // Test structure
77 // Test structure
66 QTest::addColumn<DataContainer>("inputData"); // array data's input
78 QTest::addColumn<InputData>("inputData"); // array data's input
67 QTest::addColumn<bool>("success"); // array data has been successfully constructed
79 QTest::addColumn<bool>("success"); // array data has been successfully constructed
68 QTest::addColumn<DataContainer>("expectedData"); // expected array data (when success)
80 QTest::addColumn<Container>("expectedData"); // expected array data (when success)
69
81
70 // Test cases
82 // Test cases
71 QTest::newRow("validInput")
83 QTest::newRow("validInput") << flatten(Container{{1., 2., 3., 4., 5.},
72 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
84 {6., 7., 8., 9., 10.},
73 << true
85 {11., 12., 13., 14., 15.}})
74 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
86 << true << Container{{1., 2., 3., 4., 5.},
75 QTest::newRow("malformedInput (components of the array data haven't the same size")
87 {6., 7., 8., 9., 10.},
76 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8.}, {11., 12.}} << true
88 {11., 12., 13., 14., 15.}};
77 << DataContainer{{}, {}, {}};
89 QTest::newRow("invalidInput (invalid data size")
78 QTest::newRow("invalidInput (less than tow components") << DataContainer{{1., 2., 3., 4., 5.}}
90 << InputData{{1., 2., 3., 4., 5., 6., 7.}, 3} << false << Container{{}, {}, {}};
79 << false << DataContainer{{}, {}, {}};
91 QTest::newRow("invalidInput (less than two components")
92 << flatten(Container{{1., 2., 3., 4., 5.}}) << false << Container{{}, {}, {}};
80 }
93 }
81
94
82 void TestTwoDimArrayData::testCtor()
95 void TestTwoDimArrayData::testCtor()
83 {
96 {
84 QFETCH(DataContainer, inputData);
97 QFETCH(InputData, inputData);
85 QFETCH(bool, success);
98 QFETCH(bool, success);
86
99
87 if (success) {
100 if (success) {
88 QFETCH(DataContainer, expectedData);
101 QFETCH(Container, expectedData);
89
90 ArrayData<2> arrayData{inputData};
91
102
92 for (auto i = 0; i < expectedData.size(); ++i) {
103 ArrayData<2> arrayData{inputData.first, inputData.second};
93 QVERIFY(arrayData.data(i) == expectedData.at(i));
104 verifyArrayData(arrayData, expectedData);
94 }
95 }
105 }
96 else {
106 else {
97 QVERIFY_EXCEPTION_THROWN(ArrayData<2> arrayData{inputData}, std::invalid_argument);
107 QVERIFY_EXCEPTION_THROWN(ArrayData<2>(inputData.first, inputData.second),
108 std::invalid_argument);
98 }
109 }
99 }
110 }
100
111
101 void TestTwoDimArrayData::testAdd_data()
112 void TestTwoDimArrayData::testAdd_data()
102 {
113 {
103 // Test structure
114 // Test structure
104 QTest::addColumn<DataContainer>("inputData"); // array's data input
115 QTest::addColumn<InputData>("inputData"); // array's data input
105 QTest::addColumn<DataContainer>("otherData"); // array data's input to merge with
116 QTest::addColumn<InputData>("otherData"); // array data's input to merge with
106 QTest::addColumn<bool>("prepend"); // prepend or append merge
117 QTest::addColumn<bool>("prepend"); // prepend or append merge
107 QTest::addColumn<DataContainer>("expectedData"); // expected data after merge
118 QTest::addColumn<Container>("expectedData"); // expected data after merge
108
119
109 // Test cases
120 // Test cases
110 auto inputData
121 auto inputData = flatten(
111 = DataContainer{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}};
122 Container{{1., 2., 3., 4., 5.}, {11., 12., 13., 14., 15.}, {21., 22., 23., 24., 25.}});
112
123
113 auto vectorContainer = DataContainer{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}};
124 auto vectorContainer = flatten(Container{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28}});
114 auto tensorContainer = DataContainer{{6., 7., 8.}, {16., 17., 18.}, {26., 27., 28},
125 auto tensorContainer = flatten(Container{{6., 7., 8.},
115 {36., 37., 38.}, {46., 47., 48.}, {56., 57., 58}};
126 {16., 17., 18.},
127 {26., 27., 28},
128 {36., 37., 38.},
129 {46., 47., 48.},
130 {56., 57., 58}});
116
131
117 QTest::newRow("appendMerge") << inputData << vectorContainer << false
132 QTest::newRow("appendMerge") << inputData << vectorContainer << false
118 << DataContainer{{1., 2., 3., 4., 5., 6., 7., 8.},
133 << Container{{1., 2., 3., 4., 5., 6., 7., 8.},
119 {11., 12., 13., 14., 15., 16., 17., 18.},
134 {11., 12., 13., 14., 15., 16., 17., 18.},
120 {21., 22., 23., 24., 25., 26., 27., 28}};
135 {21., 22., 23., 24., 25., 26., 27., 28}};
121 QTest::newRow("prependMerge") << inputData << vectorContainer << true
136 QTest::newRow("prependMerge") << inputData << vectorContainer << true
122 << DataContainer{{6., 7., 8., 1., 2., 3., 4., 5.},
137 << Container{{6., 7., 8., 1., 2., 3., 4., 5.},
123 {16., 17., 18., 11., 12., 13., 14., 15.},
138 {16., 17., 18., 11., 12., 13., 14., 15.},
124 {26., 27., 28, 21., 22., 23., 24., 25.}};
139 {26., 27., 28, 21., 22., 23., 24., 25.}};
125 QTest::newRow("invalidMerge") << inputData << tensorContainer << false << inputData;
140 QTest::newRow("invalidMerge") << inputData << tensorContainer << false
141 << Container{{1., 2., 3., 4., 5.},
142 {11., 12., 13., 14., 15.},
143 {21., 22., 23., 24., 25.}};
126 }
144 }
127
145
128 void TestTwoDimArrayData::testAdd()
146 void TestTwoDimArrayData::testAdd()
129 {
147 {
130 QFETCH(DataContainer, inputData);
148 QFETCH(InputData, inputData);
131 QFETCH(DataContainer, otherData);
149 QFETCH(InputData, otherData);
132 QFETCH(bool, prepend);
150 QFETCH(bool, prepend);
133 QFETCH(DataContainer, expectedData);
151 QFETCH(Container, expectedData);
134
152
135 ArrayData<2> arrayData{inputData};
153 ArrayData<2> arrayData{inputData.first, inputData.second};
136 ArrayData<2> other{otherData};
154 ArrayData<2> other{otherData.first, otherData.second};
137
155
138 arrayData.add(other, prepend);
156 arrayData.add(other, prepend);
139
157
140 for (auto i = 0; i < expectedData.size(); ++i) {
158 verifyArrayData(arrayData, expectedData);
141 QVERIFY(arrayData.data(i) == expectedData.at(i));
142 }
143 }
159 }
144
160
145 void TestTwoDimArrayData::testClear_data()
161 void TestTwoDimArrayData::testClear_data()
146 {
162 {
147 // Test structure
163 // Test structure
148 QTest::addColumn<DataContainer>("inputData"); // array data's input
164 QTest::addColumn<InputData>("inputData"); // array data's input
149
165
150 // Test cases
166 // Test cases
151 QTest::newRow("data1") << DataContainer{
167 QTest::newRow("data1") << flatten(
152 {1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}};
168 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}});
153 }
169 }
154
170
155 void TestTwoDimArrayData::testClear()
171 void TestTwoDimArrayData::testClear()
156 {
172 {
157 QFETCH(DataContainer, inputData);
173 QFETCH(InputData, inputData);
158
174
159 ArrayData<2> arrayData{inputData};
175 ArrayData<2> arrayData{inputData.first, inputData.second};
160 arrayData.clear();
176 arrayData.clear();
161
177
162 for (auto i = 0; i < inputData.size(); ++i) {
178 auto emptyData = Container(inputData.second, QVector<double>{});
163 QVERIFY(arrayData.data(i) == QVector<double>{});
179 verifyArrayData(arrayData, emptyData);
164 }
165 }
180 }
166
181
167 void TestTwoDimArrayData::testSize_data()
182 void TestTwoDimArrayData::testSize_data()
168 {
183 {
169 // Test structure
184 // Test structure
170 QTest::addColumn<QVector<QVector<double> > >("inputData"); // array data's input
185 QTest::addColumn<InputData>("inputData"); // array data's input
171 QTest::addColumn<int>("expectedSize"); // expected array data size
186 QTest::addColumn<int>("expectedSize"); // expected array data size
172
187
173 // Test cases
188 // Test cases
174 QTest::newRow("data1") << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}} << 5;
189 QTest::newRow("data1") << flatten(Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}}) << 5;
175 QTest::newRow("data2") << DataContainer{{1., 2., 3., 4., 5.},
190 QTest::newRow("data2") << flatten(Container{{1., 2., 3., 4., 5.},
176 {6., 7., 8., 9., 10.},
191 {6., 7., 8., 9., 10.},
177 {11., 12., 13., 14., 15.}}
192 {11., 12., 13., 14., 15.}})
178 << 5;
193 << 5;
179 }
194 }
180
195
181 void TestTwoDimArrayData::testSize()
196 void TestTwoDimArrayData::testSize()
182 {
197 {
183 QFETCH(DataContainer, inputData);
198 QFETCH(InputData, inputData);
184 QFETCH(int, expectedSize);
199 QFETCH(int, expectedSize);
185
200
186 ArrayData<2> arrayData{inputData};
201 ArrayData<2> arrayData{inputData.first, inputData.second};
187 QVERIFY(arrayData.size() == expectedSize);
202 QVERIFY(arrayData.size() == expectedSize);
188 }
203 }
189
204
190 void TestTwoDimArrayData::testSort_data()
205 void TestTwoDimArrayData::testSort_data()
191 {
206 {
192 // Test structure
207 // Test structure
193 QTest::addColumn<DataContainer>("inputData"); // array data's input
208 QTest::addColumn<InputData>("inputData"); // array data's input
194 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
209 QTest::addColumn<std::vector<int> >("sortPermutation"); // permutation used to sort data
195 QTest::addColumn<DataContainer>("expectedData"); // expected data after sorting
210 QTest::addColumn<Container>("expectedData"); // expected data after sorting
196
211
197 // Test cases
212 // Test cases
198 QTest::newRow("data1")
213 QTest::newRow("data1")
199 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
214 << flatten(
215 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
200 << std::vector<int>{0, 2, 3, 1, 4}
216 << std::vector<int>{0, 2, 3, 1, 4}
201 << DataContainer{{1., 3., 4., 2., 5.}, {6., 8., 9., 7., 10.}, {11., 13., 14., 12., 15.}};
217 << Container{{1., 3., 4., 2., 5.}, {6., 8., 9., 7., 10.}, {11., 13., 14., 12., 15.}};
202 QTest::newRow("data2")
218 QTest::newRow("data2")
203 << DataContainer{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}}
219 << flatten(
220 Container{{1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.}, {11., 12., 13., 14., 15.}})
204 << std::vector<int>{2, 4, 3, 0, 1}
221 << std::vector<int>{2, 4, 3, 0, 1}
205 << DataContainer{{3., 5., 4., 1., 2.}, {8., 10., 9., 6., 7.}, {13., 15., 14., 11., 12.}};
222 << Container{{3., 5., 4., 1., 2.}, {8., 10., 9., 6., 7.}, {13., 15., 14., 11., 12.}};
206 }
223 }
207
224
208 void TestTwoDimArrayData::testSort()
225 void TestTwoDimArrayData::testSort()
209 {
226 {
210 QFETCH(DataContainer, inputData);
227 QFETCH(InputData, inputData);
211 QFETCH(std::vector<int>, sortPermutation);
228 QFETCH(std::vector<int>, sortPermutation);
212 QFETCH(DataContainer, expectedData);
229 QFETCH(Container, expectedData);
213
230
214 ArrayData<2> arrayData{inputData};
231 ArrayData<2> arrayData{inputData.first, inputData.second};
215 auto sortedArrayData = arrayData.sort(sortPermutation);
232 auto sortedArrayData = arrayData.sort(sortPermutation);
216 QVERIFY(sortedArrayData != nullptr);
233 QVERIFY(sortedArrayData != nullptr);
217
234
218 for (auto i = 0; i < expectedData.size(); ++i) {
235 verifyArrayData(*sortedArrayData, expectedData);
219 QVERIFY(sortedArrayData->data(i) == expectedData.at(i));
220 }
221 }
236 }
222
237
223 QTEST_MAIN(TestTwoDimArrayData)
238 QTEST_MAIN(TestTwoDimArrayData)
224 #include "TestTwoDimArrayData.moc"
239 #include "TestTwoDimArrayData.moc"
@@ -1,310 +1,281
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/VectorSeries.h>
4 #include <Data/VectorSeries.h>
5
5
6 #include <QObject>
6 #include <QObject>
7 #include <QtTest>
7 #include <QtTest>
8
8
9 namespace {
9 namespace {
10
10
11 /// Path for the tests
11 /// Path for the tests
12 const auto TESTS_RESOURCES_PATH
12 const auto TESTS_RESOURCES_PATH
13 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
14
14
15 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
15 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 {
16 {
17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 }
18 }
19
19
20 /// Compares two vectors that can potentially contain NaN values
21 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
22 {
23 if (v1.size() != v2.size()) {
24 return false;
25 }
26
27 auto result = true;
28 auto v2It = v2.cbegin();
29 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
30 auto v1Value = *v1It;
31 auto v2Value = *v2It;
32
33 // If v1 is NaN, v2 has to be NaN too
34 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
35 }
36
37 return result;
38 }
39
40 bool compareVectors(const QVector<QVector<double> > &v1, const QVector<QVector<double> > &v2)
41 {
42 if (v1.size() != v2.size()) {
43 return false;
44 }
45
46 auto result = true;
47 for (auto i = 0; i < v1.size() && result; ++i) {
48 result &= compareVectors(v1.at(i), v2.at(i));
49 }
50
51 return result;
52 }
53
54 QVector<QVector<double> > valuesData(const ArrayData<1> &arrayData)
55 {
56 return QVector<QVector<double> >{arrayData.data()};
57 }
58
59 QVector<QVector<double> > valuesData(const ArrayData<2> &arrayData)
60 {
61 return arrayData.data();
62 }
63
64
65 QString inputFilePath(const QString &inputFileName)
20 QString inputFilePath(const QString &inputFileName)
66 {
21 {
67 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
22 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
68 }
23 }
69
24
70 template <typename T>
25 template <typename T>
71 struct ExpectedResults {
26 struct ExpectedResults {
72 explicit ExpectedResults() = default;
27 explicit ExpectedResults() = default;
73
28
74 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
29 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
75 QVector<double> valuesData)
30 QVector<double> valuesData)
76 : ExpectedResults(xAxisUnit, valuesUnit, xAxisData,
31 : ExpectedResults(xAxisUnit, valuesUnit, xAxisData,
77 QVector<QVector<double> >{std::move(valuesData)})
32 QVector<QVector<double> >{std::move(valuesData)})
78 {
33 {
79 }
34 }
80
35
81 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
36 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
82 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
37 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
83 QVector<QVector<double> > valuesData)
38 QVector<QVector<double> > valuesData)
84 : m_ParsingOK{true},
39 : m_ParsingOK{true},
85 m_XAxisUnit{xAxisUnit},
40 m_XAxisUnit{xAxisUnit},
86 m_ValuesUnit{valuesUnit},
41 m_ValuesUnit{valuesUnit},
87 m_XAxisData{},
42 m_XAxisData{},
88 m_ValuesData{std::move(valuesData)}
43 m_ValuesData{std::move(valuesData)}
89 {
44 {
90 // Converts QVector<QDateTime> to QVector<double>
45 // Converts QVector<QDateTime> to QVector<double>
91 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
46 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
92 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
47 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
93 }
48 }
94
49
95 /**
50 /**
96 * Validates a DataSeries compared to the expected results
51 * Validates a DataSeries compared to the expected results
97 * @param results the DataSeries to validate
52 * @param results the DataSeries to validate
98 */
53 */
99 void validate(std::shared_ptr<IDataSeries> results)
54 void validate(std::shared_ptr<IDataSeries> results)
100 {
55 {
101 if (m_ParsingOK) {
56 if (m_ParsingOK) {
102 auto dataSeries = dynamic_cast<T *>(results.get());
57 auto dataSeries = dynamic_cast<T *>(results.get());
103 QVERIFY(dataSeries != nullptr);
58 QVERIFY(dataSeries != nullptr);
104
59
105 // Checks units
60 // Checks units
106 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
61 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
107 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
62 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
108
63
109 // Checks values : as the vectors can potentially contain NaN values, we must use a
64 auto verifyRange = [dataSeries](const auto &expectedData, const auto &equalFun) {
110 // custom vector comparison method
65 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), expectedData.cbegin(),
111 QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData));
66 expectedData.cend(),
112 QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData));
67 [&equalFun](const auto &dataSeriesIt, const auto &expectedX) {
68 return equalFun(dataSeriesIt, expectedX);
69 }));
70 };
71
72 // Checks x-axis data
73 verifyRange(m_XAxisData, [](const auto &seriesIt, const auto &value) {
74 return seriesIt.x() == value;
75 });
76
77 // Checks values data of each component
78 for (auto i = 0; i < m_ValuesData.size(); ++i) {
79 verifyRange(m_ValuesData.at(i), [i](const auto &seriesIt, const auto &value) {
80 auto itValue = seriesIt.value(i);
81 return (std::isnan(itValue) && std::isnan(value)) || seriesIt.value(i) == value;
82 });
83 }
113 }
84 }
114 else {
85 else {
115 QVERIFY(results == nullptr);
86 QVERIFY(results == nullptr);
116 }
87 }
117 }
88 }
118
89
119 // Parsing was successfully completed
90 // Parsing was successfully completed
120 bool m_ParsingOK{false};
91 bool m_ParsingOK{false};
121 // Expected x-axis unit
92 // Expected x-axis unit
122 Unit m_XAxisUnit{};
93 Unit m_XAxisUnit{};
123 // Expected values unit
94 // Expected values unit
124 Unit m_ValuesUnit{};
95 Unit m_ValuesUnit{};
125 // Expected x-axis data
96 // Expected x-axis data
126 QVector<double> m_XAxisData{};
97 QVector<double> m_XAxisData{};
127 // Expected values data
98 // Expected values data
128 QVector<QVector<double> > m_ValuesData{};
99 QVector<QVector<double> > m_ValuesData{};
129 };
100 };
130
101
131 } // namespace
102 } // namespace
132
103
133 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
104 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
134 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
105 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
135
106
136 class TestAmdaResultParser : public QObject {
107 class TestAmdaResultParser : public QObject {
137 Q_OBJECT
108 Q_OBJECT
138 private:
109 private:
139 template <typename T>
110 template <typename T>
140 void testReadDataStructure()
111 void testReadDataStructure()
141 {
112 {
142 // ////////////// //
113 // ////////////// //
143 // Test structure //
114 // Test structure //
144 // ////////////// //
115 // ////////////// //
145
116
146 // Name of TXT file to read
117 // Name of TXT file to read
147 QTest::addColumn<QString>("inputFileName");
118 QTest::addColumn<QString>("inputFileName");
148 // Expected results
119 // Expected results
149 QTest::addColumn<ExpectedResults<T> >("expectedResults");
120 QTest::addColumn<ExpectedResults<T> >("expectedResults");
150 }
121 }
151
122
152 template <typename T>
123 template <typename T>
153 void testRead(AmdaResultParser::ValueType valueType)
124 void testRead(AmdaResultParser::ValueType valueType)
154 {
125 {
155 QFETCH(QString, inputFileName);
126 QFETCH(QString, inputFileName);
156 QFETCH(ExpectedResults<T>, expectedResults);
127 QFETCH(ExpectedResults<T>, expectedResults);
157
128
158 // Parses file
129 // Parses file
159 auto filePath = inputFilePath(inputFileName);
130 auto filePath = inputFilePath(inputFileName);
160 auto results = AmdaResultParser::readTxt(filePath, valueType);
131 auto results = AmdaResultParser::readTxt(filePath, valueType);
161
132
162 // ///////////////// //
133 // ///////////////// //
163 // Validates results //
134 // Validates results //
164 // ///////////////// //
135 // ///////////////// //
165 expectedResults.validate(results);
136 expectedResults.validate(results);
166 }
137 }
167
138
168 private slots:
139 private slots:
169 /// Input test data
140 /// Input test data
170 /// @sa testReadScalarTxt()
141 /// @sa testReadScalarTxt()
171 void testReadScalarTxt_data();
142 void testReadScalarTxt_data();
172
143
173 /// Tests parsing scalar series of a TXT file
144 /// Tests parsing scalar series of a TXT file
174 void testReadScalarTxt();
145 void testReadScalarTxt();
175
146
176 /// Input test data
147 /// Input test data
177 /// @sa testReadVectorTxt()
148 /// @sa testReadVectorTxt()
178 void testReadVectorTxt_data();
149 void testReadVectorTxt_data();
179
150
180 /// Tests parsing vector series of a TXT file
151 /// Tests parsing vector series of a TXT file
181 void testReadVectorTxt();
152 void testReadVectorTxt();
182 };
153 };
183
154
184 void TestAmdaResultParser::testReadScalarTxt_data()
155 void TestAmdaResultParser::testReadScalarTxt_data()
185 {
156 {
186 testReadDataStructure<ScalarSeries>();
157 testReadDataStructure<ScalarSeries>();
187
158
188 // ////////// //
159 // ////////// //
189 // Test cases //
160 // Test cases //
190 // ////////// //
161 // ////////// //
191
162
192 // Valid files
163 // Valid files
193 QTest::newRow("Valid file")
164 QTest::newRow("Valid file")
194 << QStringLiteral("ValidScalar1.txt")
165 << QStringLiteral("ValidScalar1.txt")
195 << ExpectedResults<ScalarSeries>{
166 << ExpectedResults<ScalarSeries>{
196 Unit{QStringLiteral("nT"), true}, Unit{},
167 Unit{QStringLiteral("nT"), true}, Unit{},
197 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
168 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
198 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
169 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
199 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
170 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
200 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
171 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
201 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
172 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
202 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
173 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
203 -2.55800, -2.43250, -2.42200}};
174 -2.55800, -2.43250, -2.42200}};
204
175
205 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
176 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
206 << QStringLiteral("WrongValue.txt")
177 << QStringLiteral("WrongValue.txt")
207 << ExpectedResults<ScalarSeries>{
178 << ExpectedResults<ScalarSeries>{
208 Unit{QStringLiteral("nT"), true}, Unit{},
179 Unit{QStringLiteral("nT"), true}, Unit{},
209 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
180 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
210 dateTime(2013, 9, 23, 9, 2, 30)},
181 dateTime(2013, 9, 23, 9, 2, 30)},
211 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
182 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
212
183
213 QTest::newRow("Valid file that contains NaN values")
184 QTest::newRow("Valid file that contains NaN values")
214 << QStringLiteral("NaNValue.txt")
185 << QStringLiteral("NaNValue.txt")
215 << ExpectedResults<ScalarSeries>{
186 << ExpectedResults<ScalarSeries>{
216 Unit{QStringLiteral("nT"), true}, Unit{},
187 Unit{QStringLiteral("nT"), true}, Unit{},
217 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
188 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
218 dateTime(2013, 9, 23, 9, 2, 30)},
189 dateTime(2013, 9, 23, 9, 2, 30)},
219 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
190 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
220
191
221 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
192 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
222 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
193 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
223 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
194 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
224 Unit{}, QVector<QDateTime>{},
195 Unit{}, QVector<QDateTime>{},
225 QVector<double>{}};
196 QVector<double>{}};
226 QTest::newRow("Wrong unit file")
197 QTest::newRow("Wrong unit file")
227 << QStringLiteral("WrongUnit.txt")
198 << QStringLiteral("WrongUnit.txt")
228 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
199 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
229 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
200 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
230 dateTime(2013, 9, 23, 9, 1, 30),
201 dateTime(2013, 9, 23, 9, 1, 30),
231 dateTime(2013, 9, 23, 9, 2, 30)},
202 dateTime(2013, 9, 23, 9, 2, 30)},
232 QVector<double>{-2.83950, -2.71850, -2.52150}};
203 QVector<double>{-2.83950, -2.71850, -2.52150}};
233
204
234 QTest::newRow("Wrong results file (date of first line is invalid")
205 QTest::newRow("Wrong results file (date of first line is invalid")
235 << QStringLiteral("WrongDate.txt")
206 << QStringLiteral("WrongDate.txt")
236 << ExpectedResults<ScalarSeries>{
207 << ExpectedResults<ScalarSeries>{
237 Unit{QStringLiteral("nT"), true}, Unit{},
208 Unit{QStringLiteral("nT"), true}, Unit{},
238 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
209 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
239 QVector<double>{-2.71850, -2.52150}};
210 QVector<double>{-2.71850, -2.52150}};
240
211
241 QTest::newRow("Wrong results file (too many values for first line")
212 QTest::newRow("Wrong results file (too many values for first line")
242 << QStringLiteral("TooManyValues.txt")
213 << QStringLiteral("TooManyValues.txt")
243 << ExpectedResults<ScalarSeries>{
214 << ExpectedResults<ScalarSeries>{
244 Unit{QStringLiteral("nT"), true}, Unit{},
215 Unit{QStringLiteral("nT"), true}, Unit{},
245 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
216 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
246 QVector<double>{-2.71850, -2.52150}};
217 QVector<double>{-2.71850, -2.52150}};
247
218
248 QTest::newRow("Wrong results file (x of first line is NaN")
219 QTest::newRow("Wrong results file (x of first line is NaN")
249 << QStringLiteral("NaNX.txt")
220 << QStringLiteral("NaNX.txt")
250 << ExpectedResults<ScalarSeries>{
221 << ExpectedResults<ScalarSeries>{
251 Unit{QStringLiteral("nT"), true}, Unit{},
222 Unit{QStringLiteral("nT"), true}, Unit{},
252 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
223 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
253 QVector<double>{-2.71850, -2.52150}};
224 QVector<double>{-2.71850, -2.52150}};
254
225
255 QTest::newRow("Invalid file type (vector)")
226 QTest::newRow("Invalid file type (vector)")
256 << QStringLiteral("ValidVector1.txt")
227 << QStringLiteral("ValidVector1.txt")
257 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
228 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
258 QVector<QDateTime>{}, QVector<double>{}};
229 QVector<QDateTime>{}, QVector<double>{}};
259
230
260 // Invalid files
231 // Invalid files
261 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
232 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
262 << ExpectedResults<ScalarSeries>{};
233 << ExpectedResults<ScalarSeries>{};
263
234
264 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
235 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
265 << ExpectedResults<ScalarSeries>{};
236 << ExpectedResults<ScalarSeries>{};
266 }
237 }
267
238
268 void TestAmdaResultParser::testReadScalarTxt()
239 void TestAmdaResultParser::testReadScalarTxt()
269 {
240 {
270 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
241 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
271 }
242 }
272
243
273 void TestAmdaResultParser::testReadVectorTxt_data()
244 void TestAmdaResultParser::testReadVectorTxt_data()
274 {
245 {
275 testReadDataStructure<VectorSeries>();
246 testReadDataStructure<VectorSeries>();
276
247
277 // ////////// //
248 // ////////// //
278 // Test cases //
249 // Test cases //
279 // ////////// //
250 // ////////// //
280
251
281 // Valid files
252 // Valid files
282 QTest::newRow("Valid file")
253 QTest::newRow("Valid file")
283 << QStringLiteral("ValidVector1.txt")
254 << QStringLiteral("ValidVector1.txt")
284 << ExpectedResults<VectorSeries>{
255 << ExpectedResults<VectorSeries>{
285 Unit{QStringLiteral("nT"), true}, Unit{},
256 Unit{QStringLiteral("nT"), true}, Unit{},
286 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
257 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
287 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
258 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
288 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
259 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
289 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
260 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
290 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
261 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
291 QVector<QVector<double> >{
262 QVector<QVector<double> >{
292 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
263 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
293 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
264 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
294 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
265 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
295
266
296 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
267 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
297 QTest::newRow("Invalid file type (scalar)")
268 QTest::newRow("Invalid file type (scalar)")
298 << QStringLiteral("ValidScalar1.txt")
269 << QStringLiteral("ValidScalar1.txt")
299 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
270 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
300 QVector<QDateTime>{},
271 QVector<QDateTime>{},
301 QVector<QVector<double> >{{}, {}, {}}};
272 QVector<QVector<double> >{{}, {}, {}}};
302 }
273 }
303
274
304 void TestAmdaResultParser::testReadVectorTxt()
275 void TestAmdaResultParser::testReadVectorTxt()
305 {
276 {
306 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
277 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
307 }
278 }
308
279
309 QTEST_MAIN(TestAmdaResultParser)
280 QTEST_MAIN(TestAmdaResultParser)
310 #include "TestAmdaResultParser.moc"
281 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now