Belle II Software development
mapHelperFunctions.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9// google test framework
10#include <gtest/gtest.h>
11
12#include <tracking/spacePointCreation/MapHelperFunctions.h>
13#include <framework/logging/Logger.h> // for B2INFO, etc...
14
15#include <unordered_map>
16// #include <map> // only testing unordered maps here, but should be the same for maps (only slower)
17#include <cmath>
18#include <numeric>
19
20using namespace std;
21
22namespace Belle2 {
28 typedef std::unordered_multimap<int, double> i2dMultiMap;
29 typedef std::unordered_map<int, double> i2dMap;
36 template<typename Functor>
37 i2dMultiMap createMultiMap(int nEntries, Functor funct)
38 {
39 i2dMultiMap map;
40 for (int i = 0; i < nEntries; ++i) {
41 map.insert(std::make_pair((i % 6), funct.operator()(i)));
42 }
43 return map;
44 }
45
51 template<typename Functor>
52 i2dMap createMap(int nEntries, Functor funct)
53 {
54 i2dMap map;
55 for (int i = 0; i < nEntries; ++i) {
56 map.insert(std::make_pair(i, funct.operator()(i)));
57 }
58 return map;
59 }
60
65 // Silence Doxygen which is complaining that "no uniquely matching class member found for"
66 // But there should be a better way that I just don't know of / find
68 struct {
69 double operator()(int i)
70 {
71 return std::tan(i) / std::sin(i);
72 }
73 } secans;
75
77 // Silence Doxygen which is complaining that "no uniquely matching class member found for".
78 // But there should be a better way that I just don't know of / find
80 struct {
81 double operator()(int i)
82 {
83 return std::sin(i * .5);
84 }
85 } sinHalf;
87
91 class MapHelperFunctionsTest : public ::testing::Test {
92 protected:
94 virtual void SetUp()
95 {
96 _nEntries = 20;
97 _map = createMap(_nEntries, sinHalf);
99 _nanMap = createMap(_nEntries, secans);
101 }
102
108 };
109
111 TEST_F(MapHelperFunctionsTest, testCreatorFunctions)
112 {
113 unsigned int N = _nEntries; // same number as in setup
114 EXPECT_EQ(N, _nanMultiMap.size()); // test if creation worked (very basically by comparing the size of the returned _nanMap)
115 EXPECT_EQ(N, _nanMap.size());
116 EXPECT_EQ(N, _multimap.size());
117 EXPECT_EQ(N, _map.size());
118
119 ASSERT_TRUE(std::isnan(secans.operator()(0))) << "For the following tests to work properly this is expected to return a NaN (-nan)";
120
121// std::cout << printMap(_nanMap) << std::endl;
122// std::cout << printMap(_nanMultiMap) << std::endl;
123// std::cout << printMap(_multimap) << std::endl;
124// std::cout << printMap(_map) << std::endl;
125
126 // do some hard-coded queries to see if the _nanMaps were filled according to specified method
127 std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
128 for (int key : possibleKeys) {
129 std::vector<double> nanPossibleValues, possibleValues;
130 for (int arg = key; arg < _nEntries; arg += 6) {
131 nanPossibleValues.push_back(secans.operator()(arg));
132 possibleValues.push_back(sinHalf.operator()(arg));
133 }
134 // test the multimap with nan first
135 auto keyRange = _nanMultiMap.equal_range(key);
136 // check the numbers of stored values and of possible values
137 EXPECT_EQ(nanPossibleValues.size(), std::distance(keyRange.first, keyRange.second));
138 i2dMultiMap::iterator multiMapIt = keyRange.first;
139 for (int i = 0; i < std::distance(keyRange.first, keyRange.second); ++i) {
140 if (!std::isnan(multiMapIt->second)) { // cannot find nan value with std::find (NaN == NaN is always false)
141 auto findIt = std::find(nanPossibleValues.begin(), nanPossibleValues.end(), multiMapIt->second);
142 EXPECT_FALSE(findIt == nanPossibleValues.end()); // all stored values have to be possible
143 } else { // check if there is at least on NaN value in the possible Values
144 EXPECT_TRUE(std::count_if(nanPossibleValues.begin(), nanPossibleValues.end(),
145 [](const double & val) { return std::isnan(val); }) > 0);
146 }
147 ++multiMapIt;
148 }
149 keyRange = _multimap.equal_range(key);
150 EXPECT_EQ(possibleValues.size(), std::distance(keyRange.first, keyRange.second));
151 multiMapIt = keyRange.first;
152 for (int i = 0; i < std::distance(keyRange.first, keyRange.second); ++i) {
153 auto findIt = std::find(possibleValues.begin(), possibleValues.end(), multiMapIt->second);
154 EXPECT_FALSE(findIt == possibleValues.end());
155 ++multiMapIt;
156 }
157 }
158
159 for (unsigned int i = 0; i < N; ++i) {
160 auto range = _nanMap.equal_range(i);
161 i2dMap::iterator mapIt = range.first;
162 for (int j = 0; j < std::distance(range.first, range.second); ++j) {
163 if (std::isnan(mapIt->second)) {
164 B2INFO("Not Comparing value to key " << i << " because value is NaN, checking if the functor returns NaN for this key");
165 EXPECT_TRUE(std::isnan(secans.operator()(i))); // expect a NaN value from the operator
166 continue; // do not compare NaNs! (will always fail due to their nature)
167 }
168 EXPECT_DOUBLE_EQ(mapIt->second, secans.operator()(i));
169 ++mapIt;
170 }
171
172 range = _map.equal_range(i);
173 mapIt = range.first;
174 for (int j = 0; j < std::distance(range.first, range.second); ++j) {
175 EXPECT_DOUBLE_EQ(mapIt->second, sinHalf.operator()(i));
176 }
177 }
178 }
179
181 TEST_F(MapHelperFunctionsTest, testGetUniqueKeys)
182 {
183 // get the unique keys for the map (assuming that the key_type is int!)
184 std::vector<int> uniqueKeys = getUniqueKeys(_map);
185 EXPECT_EQ(uniqueKeys.size(), _map.size()); // for the map the number of uniqueKeys has to be the same as the size!
186 for (size_t i = 0; i < uniqueKeys.size(); ++i) {
187 // using std::find because in this way there is no assumption on the order of the keys!
188 auto findIt = std::find(uniqueKeys.begin(), uniqueKeys.end(), i);
189 EXPECT_FALSE(findIt == uniqueKeys.end());
190 }
191
192 // get the unique keys for the multimap
193 std::vector<int> uniqueMultiKeys = getUniqueKeys(_multimap);
194 unsigned int expectedSize = _multimap.size() > 6 ? 6 : _multimap.size(); // (creation of the map is % 6 -> only 6 keys possible)
195 EXPECT_EQ(expectedSize, uniqueMultiKeys.size());
196 for (size_t i = 0; i < uniqueMultiKeys.size(); ++i) {
197 // using std::find because in this way there is no assumption on the order of the keys!
198 auto findIt = std::find(uniqueMultiKeys.begin(), uniqueMultiKeys.end(), i);
199 EXPECT_FALSE(findIt == uniqueMultiKeys.end());
200 }
201 }
202
204 TEST_F(MapHelperFunctionsTest, testGetValuesToKey)
205 {
206 // multimap
207 std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
208 for (int key : possibleKeys) {
209 std::vector<double> expectedValues;
210 for (int arg = key; arg < _nEntries; arg += 6) { expectedValues.push_back(sinHalf.operator()(arg)); }
211 std::vector<double> actualValues = getValuesToKey(_multimap, key);
212 EXPECT_EQ(expectedValues.size(), actualValues.size());
213
214 // check if all values are present
215 for (const double& d : actualValues) {
216 EXPECT_FALSE(std::find(expectedValues.begin(), expectedValues.end(), d) == expectedValues.end());
217 }
218 expectedValues.clear();
219 for (int arg = key; arg < _nEntries; arg += 6) {
220 expectedValues.push_back(secans.operator()(arg));
221 }
222 actualValues = getValuesToKey(_nanMultiMap, key);
223 EXPECT_EQ(expectedValues.size(), actualValues.size());
224
225 // check if all values are present
226 for (const double& d : actualValues) {
227 if (std::isnan(d)) {
228 B2INFO("Not comparing NaN value!");
229 continue;
230 }
231 EXPECT_FALSE(std::find(expectedValues.begin(), expectedValues.end(), d) == expectedValues.end());
232 }
233 }
234
235 // map
236 for (int i = 0; i < _nEntries; ++i) {
237 std::vector<double> values = getValuesToKey(_map, i);
238 EXPECT_EQ(values.size(), 1);
239 EXPECT_DOUBLE_EQ(values[0], sinHalf.operator()(i));
240
241 values = getValuesToKey(_nanMap, i);
242 EXPECT_EQ(values.size(), 1);
243 if (!std::isnan(values[0])) { // not checking here (already checked, that the functor leads to this above)
244 EXPECT_DOUBLE_EQ(values[0], secans.operator()(i));
245 }
246 }
247 }
248
250 TEST_F(MapHelperFunctionsTest, testGetNValuesPerKey)
251 {
252 std::vector<std::pair<int, unsigned int> > mapKeys = getNValuesPerKey(_map);
253 unsigned int oneKey2Value = std::count_if(mapKeys.begin(), mapKeys.end(),
254 [](const std::pair<int, unsigned int>& pair) { return pair.second == 1; });
255 EXPECT_EQ(_map.size(), oneKey2Value); // all keys should only be there once in a map!
256
257 mapKeys = getNValuesPerKey(_nanMap);
258 oneKey2Value = std::count_if(mapKeys.begin(), mapKeys.end(),
259 [](const std::pair<int, unsigned int>& pair) { return pair.second == 1; });
260 EXPECT_EQ(_nanMap.size(), oneKey2Value);
261
262 // multimaps (NOTE: hardcoded expectations because of knowledge how the maps should be filled!)
263 mapKeys = getNValuesPerKey(_nanMultiMap);
264 EXPECT_EQ(mapKeys.size(), 6);
265 for (const auto& key : mapKeys) {
266 EXPECT_EQ(key.second, key.first < 2 ? 4 : 3);
267 }
268
269 mapKeys = getNValuesPerKey(_multimap);
270 EXPECT_EQ(mapKeys.size(), 6);
271 for (const auto& key : mapKeys) {
272 EXPECT_EQ(key.second, key.first < 2 ? 4 : 3);
273 }
274 }
275
277 TEST_F(MapHelperFunctionsTest, testGetSortedKeyValueTuples)
278 {
279 // multimap without nans first
280 std::vector<int> possibleKeys = { 0, 1, 2, 3, 4, 5 };
281 std::vector<double> summedValues;
282 std::vector<double> summedNanValues;
283 for (int key : possibleKeys) {
284 std::vector<double> possibleValues;
285 std::vector<double> possibleNanValues;
286 for (int arg = key; arg < _nEntries; arg += 6) {
287 possibleValues.push_back(sinHalf.operator()(arg));
288 possibleNanValues.push_back(secans.operator()(arg));
289 }
290 summedNanValues.push_back(std::accumulate(possibleNanValues.begin(), possibleNanValues.end(), 0.0));
291 summedValues.push_back(std::accumulate(possibleValues.begin(), possibleValues.end(), 0.0));
292 }
293
294 std::vector<std::tuple<int, double, unsigned int> > keyValPairs = getSortedKeyValueTuples(_nanMultiMap);
295
296 EXPECT_EQ(keyValPairs.size(), 6);
297 // hardcoded checking here (only sampling here! not checking the whole thing!)
298 EXPECT_EQ(get<0>(keyValPairs[0]), 0);
299 EXPECT_TRUE(std::isnan(get<1>(keyValPairs[0])));
300 EXPECT_EQ(get<2>(keyValPairs[0]), 4);
301
302 // all the other checks with multimap without NaNs
303 keyValPairs = getSortedKeyValueTuples(_multimap);
304 EXPECT_EQ(keyValPairs.size(), 6);
305
306 EXPECT_EQ(get<0>(keyValPairs[0]), 0);
307 EXPECT_DOUBLE_EQ(get<1>(keyValPairs[0]), summedValues[0]);
308 EXPECT_EQ(get<2>(keyValPairs[0]), 4);
309
310 EXPECT_EQ(get<0>(keyValPairs[2]), 3);
311 EXPECT_DOUBLE_EQ(get<1>(keyValPairs[2]), summedValues[3]);
312 EXPECT_EQ(get<2>(keyValPairs[2]), 3);
313
314 keyValPairs = getSortedKeyValueTuples(_map);
315 EXPECT_EQ(get<0>(keyValPairs[0]), 3); // sin(x/2) has its maximum at x = n*pi, 3 is the value that comes closest to this
316 EXPECT_DOUBLE_EQ(get<1>(keyValPairs[0]), sinHalf.operator()(3));
317
318 EXPECT_EQ(get<0>(keyValPairs[19]), 9);
319 EXPECT_DOUBLE_EQ(get<1>(keyValPairs[19]), sinHalf.operator()(9));
320 }
321
323 TEST_F(MapHelperFunctionsTest, testGetAllValues)
324 {
325 // set up vectors with all values there are in the maps
326 std::vector<double> possibleValues, nanPossibleValues;
327 for (int i = 0; i < _nEntries; ++i) {
328 possibleValues.push_back(sinHalf.operator()(i));
329 nanPossibleValues.push_back(secans.operator()(i));
330 }
331 // sort vectors to be able to simply loop over them for comparison since the order is unimportant for this function
332 std::sort(possibleValues.begin(), possibleValues.end());
333 std::sort(nanPossibleValues.begin(), nanPossibleValues.end());
334
335 // test normal map first
336 std::vector<double> allValues = getAllValues(_map);
337 EXPECT_EQ(allValues.size(), _nEntries);
338 std::sort(allValues.begin(), allValues.end());
339 for (int i = 0; i < _nEntries; ++i) {
340 EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
341 }
342
343 // test multimap with non NaN entries
344 allValues = getAllValues(_multimap);
345 EXPECT_EQ(allValues.size(), _nEntries);
346 std::sort(allValues.begin(), allValues.end());
347 for (int i = 0; i < _nEntries; ++i) {
348 EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
349 }
350
351 // TODO: does not work at the moment, FIX THIS!!
352 // test the nan maps
353 // allValues = getAllValues(_nanMap);
354 // EXPECT_EQ(allValues.size(), _nEntries);
355 // std::sort(allValues.begin(), allValues.end());
356 // for(int i = 0; i < _nEntries; ++i) {
357 // EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
358 // }
359
360 // allValues = getAllValues(_nanMultiMap);
361 // EXPECT_EQ(allValues.size(), _nEntries);
362 // std::sort(allValues.begin(), allValues.end());
363 // for(int i = 0; i < _nEntries; ++i) {
364 // EXPECT_DOUBLE_EQ(allValues[i], possibleValues[i]);
365 // }
366
367
368 }
370}
function object that implements cosecans(x) == 1/cos(x) by tan(x) / sin(x).
int _nEntries
the number of pairs that will be put into the maps for the tests
i2dMultiMap _multimap
multimap filled with values obtained by sinHalf functor
virtual void SetUp()
set up the maps for the tests
i2dMultiMap _nanMultiMap
multimap filled with values obtained by secans functor -> contains a NaN!
i2dMap _nanMap
map filled with values obtained by secans functor -> contains a NaN!
i2dMap _map
map filled with values obtained by sinHalf functor
std::vector< std::pair< typename MapType::key_type, unsigned int > > getNValuesPerKey(const MapType &aMap)
get the unique keys of a map together with the number of values associated to each key.
i2dMap createMap(int nEntries, Functor funct)
create a multimap with
i2dMultiMap createMultiMap(int nEntries, Functor funct)
create a multimap with
std::unordered_map< int, double > i2dMap
typedef for less writing effort
std::vector< typename MapType::key_type > getUniqueKeys(const MapType &aMap)
get the unique keys of a map (i.e.
std::unordered_multimap< int, double > i2dMultiMap
typedef for less writing effort
std::vector< typename MapType::mapped_type > getValuesToKey(const MapType &aMap, typename MapType::key_type aKey)
get all values stored in the map for a given key
std::vector< typename MapType::mapped_type > getAllValues(const MapType &aMap)
get all values in the map (i.e.
std::vector< std::tuple< typename MapType::key_type, typename MapType::mapped_type, unsigned int > > getSortedKeyValueTuples(const MapType &aMap)
get the (key, value, number of values) tuples stored in the map, sorted after the following scheme (d...
Abstract base class for different kinds of events.
STL namespace.