xref: /petsc/src/sys/tests/ex64.cxx (revision c17579677893417114a09c907f46463db350e08e)
1*c1757967SJacob Faibussowitsch static const char help[] = "Tests UnorderedMap.\n";
2*c1757967SJacob Faibussowitsch 
3*c1757967SJacob Faibussowitsch #include <petsc/private/cpp/unordered_map.hpp>
4*c1757967SJacob Faibussowitsch #include <petscviewer.h>
5*c1757967SJacob Faibussowitsch 
6*c1757967SJacob Faibussowitsch #include <sstream> // std::ostringstream
7*c1757967SJacob Faibussowitsch #include <string>
8*c1757967SJacob Faibussowitsch #include <vector>
9*c1757967SJacob Faibussowitsch #include <algorithm> // std::sort
10*c1757967SJacob Faibussowitsch 
11*c1757967SJacob Faibussowitsch // ==========================================================================================
12*c1757967SJacob Faibussowitsch // Setup
13*c1757967SJacob Faibussowitsch // ==========================================================================================
14*c1757967SJacob Faibussowitsch 
15*c1757967SJacob Faibussowitsch // see https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
16*c1757967SJacob Faibussowitsch static inline void hash_combine(std::size_t &) noexcept { }
17*c1757967SJacob Faibussowitsch 
18*c1757967SJacob Faibussowitsch template <typename T, typename... Rest>
19*c1757967SJacob Faibussowitsch static inline void hash_combine(std::size_t &seed, const T &v, Rest &&...rest) noexcept
20*c1757967SJacob Faibussowitsch {
21*c1757967SJacob Faibussowitsch   std::hash<T> hasher;
22*c1757967SJacob Faibussowitsch   seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
23*c1757967SJacob Faibussowitsch   hash_combine(seed, std::forward<Rest>(rest)...);
24*c1757967SJacob Faibussowitsch }
25*c1757967SJacob Faibussowitsch 
26*c1757967SJacob Faibussowitsch #define MAKE_HASHABLE(type, ...) \
27*c1757967SJacob Faibussowitsch   namespace std \
28*c1757967SJacob Faibussowitsch   { \
29*c1757967SJacob Faibussowitsch   template <> \
30*c1757967SJacob Faibussowitsch   struct hash<type> { \
31*c1757967SJacob Faibussowitsch     std::size_t operator()(const type &t) const noexcept \
32*c1757967SJacob Faibussowitsch     { \
33*c1757967SJacob Faibussowitsch       std::size_t ret = 0; \
34*c1757967SJacob Faibussowitsch       hash_combine(ret, __VA_ARGS__); \
35*c1757967SJacob Faibussowitsch       return ret; \
36*c1757967SJacob Faibussowitsch     } \
37*c1757967SJacob Faibussowitsch   }; \
38*c1757967SJacob Faibussowitsch   }
39*c1757967SJacob Faibussowitsch 
40*c1757967SJacob Faibussowitsch using pair_type = std::pair<int, double>;
41*c1757967SJacob Faibussowitsch MAKE_HASHABLE(pair_type, t.first, t.second);
42*c1757967SJacob Faibussowitsch 
43*c1757967SJacob Faibussowitsch using namespace Petsc::util;
44*c1757967SJacob Faibussowitsch 
45*c1757967SJacob Faibussowitsch struct Foo {
46*c1757967SJacob Faibussowitsch   int    x{};
47*c1757967SJacob Faibussowitsch   double y{};
48*c1757967SJacob Faibussowitsch 
49*c1757967SJacob Faibussowitsch   constexpr Foo() noexcept = default;
50*c1757967SJacob Faibussowitsch   constexpr Foo(int x, double y) noexcept : x(x), y(y) { }
51*c1757967SJacob Faibussowitsch 
52*c1757967SJacob Faibussowitsch   bool operator==(const Foo &other) const noexcept { return x == other.x && y == other.y; }
53*c1757967SJacob Faibussowitsch   bool operator!=(const Foo &other) const noexcept { return !(*this == other); }
54*c1757967SJacob Faibussowitsch   bool operator<(const Foo &other) const noexcept { return std::tie(x, y) < std::tie(other.x, other.y); }
55*c1757967SJacob Faibussowitsch 
56*c1757967SJacob Faibussowitsch   PetscErrorCode to_string(std::string &buf) const noexcept
57*c1757967SJacob Faibussowitsch   {
58*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
59*c1757967SJacob Faibussowitsch     PetscCallCXX(buf = std::to_string(x) + ", " + std::to_string(y));
60*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
61*c1757967SJacob Faibussowitsch   }
62*c1757967SJacob Faibussowitsch 
63*c1757967SJacob Faibussowitsch   friend std::ostream &operator<<(std::ostream &oss, const Foo &f) noexcept
64*c1757967SJacob Faibussowitsch   {
65*c1757967SJacob Faibussowitsch     std::string ret;
66*c1757967SJacob Faibussowitsch 
67*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
68*c1757967SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, f.to_string(ret));
69*c1757967SJacob Faibussowitsch     oss << ret;
70*c1757967SJacob Faibussowitsch     PetscFunctionReturn(oss);
71*c1757967SJacob Faibussowitsch   }
72*c1757967SJacob Faibussowitsch };
73*c1757967SJacob Faibussowitsch 
74*c1757967SJacob Faibussowitsch MAKE_HASHABLE(Foo, t.x, t.y);
75*c1757967SJacob Faibussowitsch 
76*c1757967SJacob Faibussowitsch struct Bar {
77*c1757967SJacob Faibussowitsch   std::vector<int> x{};
78*c1757967SJacob Faibussowitsch   std::string      y{};
79*c1757967SJacob Faibussowitsch 
80*c1757967SJacob Faibussowitsch   Bar() noexcept = default;
81*c1757967SJacob Faibussowitsch   Bar(std::vector<int> x, std::string y) noexcept : x(std::move(x)), y(std::move(y)) { }
82*c1757967SJacob Faibussowitsch 
83*c1757967SJacob Faibussowitsch   bool operator==(const Bar &other) const noexcept { return x == other.x && y == other.y; }
84*c1757967SJacob Faibussowitsch   bool operator<(const Bar &other) const noexcept { return std::tie(x, y) < std::tie(other.x, other.y); }
85*c1757967SJacob Faibussowitsch 
86*c1757967SJacob Faibussowitsch   PetscErrorCode to_string(std::string &buf) const noexcept
87*c1757967SJacob Faibussowitsch   {
88*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
89*c1757967SJacob Faibussowitsch     PetscCallCXX(buf = '<');
90*c1757967SJacob Faibussowitsch     for (std::size_t i = 0; i < x.size(); ++i) {
91*c1757967SJacob Faibussowitsch       PetscCallCXX(buf += std::to_string(x[i]));
92*c1757967SJacob Faibussowitsch       if (i + 1 != x.size()) PetscCallCXX(buf += ", ");
93*c1757967SJacob Faibussowitsch     }
94*c1757967SJacob Faibussowitsch     PetscCallCXX(buf += ">, <" + y + '>');
95*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
96*c1757967SJacob Faibussowitsch   }
97*c1757967SJacob Faibussowitsch 
98*c1757967SJacob Faibussowitsch   friend std::ostream &operator<<(std::ostream &oss, const Bar &b) noexcept
99*c1757967SJacob Faibussowitsch   {
100*c1757967SJacob Faibussowitsch     std::string ret;
101*c1757967SJacob Faibussowitsch 
102*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
103*c1757967SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, b.to_string(ret));
104*c1757967SJacob Faibussowitsch     oss << ret;
105*c1757967SJacob Faibussowitsch     PetscFunctionReturn(oss);
106*c1757967SJacob Faibussowitsch   }
107*c1757967SJacob Faibussowitsch };
108*c1757967SJacob Faibussowitsch 
109*c1757967SJacob Faibussowitsch struct BadHash {
110*c1757967SJacob Faibussowitsch   template <typename T>
111*c1757967SJacob Faibussowitsch   constexpr std::size_t operator()(const T &) const noexcept
112*c1757967SJacob Faibussowitsch   {
113*c1757967SJacob Faibussowitsch     return 1;
114*c1757967SJacob Faibussowitsch   }
115*c1757967SJacob Faibussowitsch };
116*c1757967SJacob Faibussowitsch 
117*c1757967SJacob Faibussowitsch template <typename T>
118*c1757967SJacob Faibussowitsch struct Printer {
119*c1757967SJacob Faibussowitsch   using signature = PetscErrorCode(const T &, std::string &);
120*c1757967SJacob Faibussowitsch 
121*c1757967SJacob Faibussowitsch   mutable std::string      buffer;
122*c1757967SJacob Faibussowitsch   std::function<signature> printer;
123*c1757967SJacob Faibussowitsch 
124*c1757967SJacob Faibussowitsch   template <typename F>
125*c1757967SJacob Faibussowitsch   Printer(F &&printer) noexcept : printer(std::forward<F>(printer))
126*c1757967SJacob Faibussowitsch   {
127*c1757967SJacob Faibussowitsch   }
128*c1757967SJacob Faibussowitsch 
129*c1757967SJacob Faibussowitsch   PETSC_NODISCARD const char *operator()(const T &value) const noexcept
130*c1757967SJacob Faibussowitsch   {
131*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
132*c1757967SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, this->printer(value, this->buffer));
133*c1757967SJacob Faibussowitsch     PetscFunctionReturn(this->buffer.c_str());
134*c1757967SJacob Faibussowitsch   }
135*c1757967SJacob Faibussowitsch };
136*c1757967SJacob Faibussowitsch 
137*c1757967SJacob Faibussowitsch #if defined(__GNUC__)
138*c1757967SJacob Faibussowitsch // gcc 6.4 through 7.5 have a visibility bug:
139*c1757967SJacob Faibussowitsch //
140*c1757967SJacob Faibussowitsch // error: 'MapTester<T>::test_insert()::<lambda(MapTester<T>::value_type&)> [with T =
141*c1757967SJacob Faibussowitsch // ...]::<lambda(...)>' declared with greater visibility than the type of its field
142*c1757967SJacob Faibussowitsch // 'MapTester<T>::test_insert()::<lambda(MapTester<T>::value_type&)> [with T =
143*c1757967SJacob Faibussowitsch // ...]::<lambda(const char*, const insert_return_type&)
144*c1757967SJacob Faibussowitsch //
145*c1757967SJacob Faibussowitsch // Error message implies that the visibility of the lambda in question is  greater than the
146*c1757967SJacob Faibussowitsch // visibility of the capture list value "this".
147*c1757967SJacob Faibussowitsch //
148*c1757967SJacob Faibussowitsch // Since lambdas are translated into the classes with the operator()(...) and (it seems like)
149*c1757967SJacob Faibussowitsch // captured values are translated into the fields of this class it looks like for some reason
150*c1757967SJacob Faibussowitsch // the visibility of that class is higher than the one of those fields.
151*c1757967SJacob Faibussowitsch //
152*c1757967SJacob Faibussowitsch // see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80947
153*c1757967SJacob Faibussowitsch   #if ((__GNUC__ == 6) && (__GNUC_MINOR__ >= 4)) || ((__GNUC__ == 7) && (__GNUC_MINOR__ <= 5))
154*c1757967SJacob Faibussowitsch     #define PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND 1
155*c1757967SJacob Faibussowitsch   #endif
156*c1757967SJacob Faibussowitsch #endif
157*c1757967SJacob Faibussowitsch 
158*c1757967SJacob Faibussowitsch #ifdef PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND
159*c1757967SJacob Faibussowitsch   #pragma GCC visibility push(hidden)
160*c1757967SJacob Faibussowitsch #endif
161*c1757967SJacob Faibussowitsch template <typename... T>
162*c1757967SJacob Faibussowitsch class MapTester {
163*c1757967SJacob Faibussowitsch public:
164*c1757967SJacob Faibussowitsch   using map_type    = Petsc::UnorderedMap<T...>;
165*c1757967SJacob Faibussowitsch   using key_type    = typename map_type::key_type;
166*c1757967SJacob Faibussowitsch   using value_type  = typename map_type::value_type;
167*c1757967SJacob Faibussowitsch   using mapped_type = typename map_type::mapped_type;
168*c1757967SJacob Faibussowitsch 
169*c1757967SJacob Faibussowitsch   const PetscViewer               vwr;
170*c1757967SJacob Faibussowitsch   const std::string               map_name;
171*c1757967SJacob Faibussowitsch   Printer<key_type>               key_printer;
172*c1757967SJacob Faibussowitsch   Printer<mapped_type>            value_printer;
173*c1757967SJacob Faibussowitsch   std::function<value_type(void)> generator;
174*c1757967SJacob Faibussowitsch 
175*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode view_map(const map_type &map) const noexcept
176*c1757967SJacob Faibussowitsch   {
177*c1757967SJacob Faibussowitsch     std::ostringstream oss;
178*c1757967SJacob Faibussowitsch 
179*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
180*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << std::boolalpha);
181*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "map: '" << this->map_name << "'\n");
182*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  size: " << map.size() << '\n');
183*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  capacity: " << map.capacity() << '\n');
184*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  bucket count: " << map.bucket_count() << '\n');
185*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  empty: " << map.empty() << '\n');
186*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  flag bucket width: " << map_type::flag_bucket_width::value << '\n');
187*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  flag pairs per bucket: " << map_type::flag_pairs_per_bucket::value << '\n');
188*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  {\n");
189*c1757967SJacob Faibussowitsch     for (auto &&entry : map) PetscCallCXX(oss << "    key: [" << this->key_printer(entry.first) << "] -> [" << this->value_printer(entry.second) << "]\n");
190*c1757967SJacob Faibussowitsch     PetscCallCXX(oss << "  }\n");
191*c1757967SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(vwr, "%s", oss.str().c_str()));
192*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
193*c1757967SJacob Faibussowitsch   }
194*c1757967SJacob Faibussowitsch 
195*c1757967SJacob Faibussowitsch #define MapCheck(map__, cond__, comm__, ierr__, base_mess__, ...) \
196*c1757967SJacob Faibussowitsch   do { \
197*c1757967SJacob Faibussowitsch     if (PetscUnlikely(!(cond__))) { \
198*c1757967SJacob Faibussowitsch       PetscCall(this->view_map(map__)); \
199*c1757967SJacob Faibussowitsch       SETERRQ(comm__, ierr__, "%s: " base_mess__, this->map_name.c_str(), __VA_ARGS__); \
200*c1757967SJacob Faibussowitsch     } \
201*c1757967SJacob Faibussowitsch   } while (0)
202*c1757967SJacob Faibussowitsch 
203*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode check_size_capacity_coherent(map_type &map) const noexcept
204*c1757967SJacob Faibussowitsch   {
205*c1757967SJacob Faibussowitsch     const auto msize = map.size();
206*c1757967SJacob Faibussowitsch     const auto mcap  = map.capacity();
207*c1757967SJacob Faibussowitsch 
208*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
209*c1757967SJacob Faibussowitsch     MapCheck(map, msize == map.size(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size appears to change each time it is called! first call: %zu, second call %zu", msize, map.size());
210*c1757967SJacob Faibussowitsch     MapCheck(map, mcap == map.capacity(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity appears to change each time it is called! first call: %zu, second call %zu", mcap, map.capacity());
211*c1757967SJacob Faibussowitsch     MapCheck(map, msize >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size %zu unexpected!", msize);
212*c1757967SJacob Faibussowitsch     MapCheck(map, mcap >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu unexpected!", mcap);
213*c1757967SJacob Faibussowitsch     MapCheck(map, mcap >= msize, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu < map size %zu!", mcap, msize);
214*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
215*c1757967SJacob Faibussowitsch   }
216*c1757967SJacob Faibussowitsch 
217*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode check_size_capacity_coherent(map_type &map, std::size_t expected_size, std::size_t expected_min_capacity) const noexcept
218*c1757967SJacob Faibussowitsch   {
219*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
220*c1757967SJacob Faibussowitsch     PetscCall(check_size_capacity_coherent(map));
221*c1757967SJacob Faibussowitsch     MapCheck(map, map.size() == expected_size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map size %zu did not increase (from %zu) after insertion!", map.size(), expected_size);
222*c1757967SJacob Faibussowitsch     MapCheck(map, map.capacity() >= expected_min_capacity, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map capacity %zu did not increase (from %zu)!", map.capacity(), expected_min_capacity);
223*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
224*c1757967SJacob Faibussowitsch   }
225*c1757967SJacob Faibussowitsch 
226*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_insert(map_type &map) noexcept
227*c1757967SJacob Faibussowitsch   {
228*c1757967SJacob Faibussowitsch     auto key             = key_type{};
229*c1757967SJacob Faibussowitsch     auto value           = mapped_type{};
230*c1757967SJacob Faibussowitsch     auto size_before     = map.size();
231*c1757967SJacob Faibussowitsch     auto capacity_before = map.capacity();
232*c1757967SJacob Faibussowitsch 
233*c1757967SJacob Faibussowitsch     const auto check_all_reinsert = [&](value_type &key_value) {
234*c1757967SJacob Faibussowitsch       using insert_return_type  = std::pair<typename map_type::iterator, bool>;
235*c1757967SJacob Faibussowitsch       auto      &key            = key_value.first;
236*c1757967SJacob Faibussowitsch       auto      &value          = key_value.second;
237*c1757967SJacob Faibussowitsch       const auto key_const      = key;
238*c1757967SJacob Faibussowitsch       const auto value_const    = value;
239*c1757967SJacob Faibussowitsch       const auto pair           = std::make_pair(key_const, value_const);
240*c1757967SJacob Faibussowitsch       const auto check_reinsert = [&](const char op[], const insert_return_type &ret) {
241*c1757967SJacob Faibussowitsch         PetscFunctionBegin;
242*c1757967SJacob Faibussowitsch         MapCheck(map, !ret.second, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s reinserted key '%s'", op, this->key_printer(key));
243*c1757967SJacob Faibussowitsch         MapCheck(map, ret.first->first == key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s returned iterator key '%s' != expected '%s'", this->key_printer(ret.first->first), op, this->key_printer(key));
244*c1757967SJacob Faibussowitsch         MapCheck(map, ret.first->second == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s returned iterator value '%s' != expected '%s'", op, this->value_printer(ret.first->second), this->value_printer(value));
245*c1757967SJacob Faibussowitsch         MapCheck(map, map[key] == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s map[%s] '%s' != '%s'", op, this->key_printer(key), this->value_printer(map[key]), this->value_printer(value));
246*c1757967SJacob Faibussowitsch         MapCheck(map, map[key_const] == value_const, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s changed value '%s' != expected '%s'", op, this->value_printer(map[key_const]), this->value_printer(value_const));
247*c1757967SJacob Faibussowitsch         PetscFunctionReturn(0);
248*c1757967SJacob Faibussowitsch       };
249*c1757967SJacob Faibussowitsch 
250*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
251*c1757967SJacob Faibussowitsch #define CHECK_REINSERT(...) check_reinsert(PetscStringize(__VA_ARGS__), __VA_ARGS__)
252*c1757967SJacob Faibussowitsch       // check the following operations don't clobber values
253*c1757967SJacob Faibussowitsch       PetscCall(CHECK_REINSERT(map.emplace(key, value)));
254*c1757967SJacob Faibussowitsch       PetscCall(CHECK_REINSERT(map.emplace(std::piecewise_construct, std::make_tuple(key), std::make_tuple(value))));
255*c1757967SJacob Faibussowitsch       PetscCall(CHECK_REINSERT(map.insert(std::make_pair(key, value))));
256*c1757967SJacob Faibussowitsch       PetscCall(CHECK_REINSERT(map.insert(pair)));
257*c1757967SJacob Faibussowitsch #undef CHECK_REINSERT
258*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
259*c1757967SJacob Faibussowitsch     };
260*c1757967SJacob Faibussowitsch 
261*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
262*c1757967SJacob Faibussowitsch     PetscCall(this->check_size_capacity_coherent(map));
263*c1757967SJacob Faibussowitsch     // put key in map
264*c1757967SJacob Faibussowitsch     PetscCallCXX(map[key] = value);
265*c1757967SJacob Faibussowitsch     // check we properly sized up
266*c1757967SJacob Faibussowitsch     PetscCall(this->check_size_capacity_coherent(map, size_before + 1, capacity_before));
267*c1757967SJacob Faibussowitsch     // and that the value matches
268*c1757967SJacob Faibussowitsch     MapCheck(map, map[key] == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map default key %s != map value %s", this->key_printer(key), this->value_printer(value));
269*c1757967SJacob Faibussowitsch     // and that the following operations don't clobber the value
270*c1757967SJacob Faibussowitsch     {
271*c1757967SJacob Faibussowitsch       value_type kv{key, value};
272*c1757967SJacob Faibussowitsch 
273*c1757967SJacob Faibussowitsch       PetscCall(check_all_reinsert(kv));
274*c1757967SJacob Faibussowitsch     }
275*c1757967SJacob Faibussowitsch 
276*c1757967SJacob Faibussowitsch     // test that clearing workings
277*c1757967SJacob Faibussowitsch     capacity_before = map.capacity();
278*c1757967SJacob Faibussowitsch     PetscCall(map.clear());
279*c1757967SJacob Faibussowitsch     // should have size = 0 (but capacity unchanged)
280*c1757967SJacob Faibussowitsch     PetscCall(this->check_size_capacity_coherent(map, 0, capacity_before));
281*c1757967SJacob Faibussowitsch 
282*c1757967SJacob Faibussowitsch     // test that all inserted values are found in the map
283*c1757967SJacob Faibussowitsch     const auto test_map_contains_expected_items = [&](std::function<PetscErrorCode(std::vector<value_type> &)> fill_map, std::size_t kv_size) {
284*c1757967SJacob Faibussowitsch       auto                     key_value_pairs = this->make_key_values(kv_size);
285*c1757967SJacob Faibussowitsch       std::vector<std::size_t> found_key_value(key_value_pairs.size());
286*c1757967SJacob Faibussowitsch 
287*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
288*c1757967SJacob Faibussowitsch       PetscCall(map.clear());
289*c1757967SJacob Faibussowitsch       PetscCall(this->check_size_capacity_coherent(map, 0, 0));
290*c1757967SJacob Faibussowitsch       PetscCall(fill_map(key_value_pairs));
291*c1757967SJacob Faibussowitsch       // map size should exactly match the size of the vector, but we don't care about capacity
292*c1757967SJacob Faibussowitsch       PetscCall(this->check_size_capacity_coherent(map, key_value_pairs.size(), 0));
293*c1757967SJacob Faibussowitsch 
294*c1757967SJacob Faibussowitsch       // sort the vector so we can use std::binary_search on it
295*c1757967SJacob Faibussowitsch       PetscCallCXX(std::sort(key_value_pairs.begin(), key_value_pairs.end()));
296*c1757967SJacob Faibussowitsch       for (auto it = map.cbegin(); it != map.cend(); ++it) {
297*c1757967SJacob Faibussowitsch         const auto kv_begin = key_value_pairs.cbegin();
298*c1757967SJacob Faibussowitsch         const auto found    = std::lower_bound(kv_begin, key_value_pairs.cend(), *it);
299*c1757967SJacob Faibussowitsch         const auto dist     = std::distance(kv_begin, found);
300*c1757967SJacob Faibussowitsch 
301*c1757967SJacob Faibussowitsch         // check that the value returned exists in our expected range
302*c1757967SJacob Faibussowitsch         MapCheck(map, found != key_value_pairs.cend(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map contained key-value pair (%s, %s) not present in input range!", this->key_printer(it->first), this->value_printer(it->second));
303*c1757967SJacob Faibussowitsch         MapCheck(map, dist >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Index of found key-value pair (%s -> %s) %td is < 0", this->key_printer(it->first), this->value_printer(it->second), static_cast<std::ptrdiff_t>(dist));
304*c1757967SJacob Faibussowitsch         // record that we found this particular entry
305*c1757967SJacob Faibussowitsch         PetscCallCXX(++found_key_value.at(static_cast<std::size_t>(dist)));
306*c1757967SJacob Faibussowitsch       }
307*c1757967SJacob Faibussowitsch 
308*c1757967SJacob Faibussowitsch       // there should only be 1 instance of each key-value in the map
309*c1757967SJacob Faibussowitsch       for (std::size_t i = 0; i < found_key_value.size(); ++i) {
310*c1757967SJacob Faibussowitsch         MapCheck(map, found_key_value[i] == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map failed to insert key %s (value %s), have find count %zu", this->key_printer(key_value_pairs[i].first), this->value_printer(key_value_pairs[i].second), found_key_value[i]);
311*c1757967SJacob Faibussowitsch       }
312*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
313*c1757967SJacob Faibussowitsch     };
314*c1757967SJacob Faibussowitsch 
315*c1757967SJacob Faibussowitsch     // clang-format off
316*c1757967SJacob Faibussowitsch     PetscCall(
317*c1757967SJacob Faibussowitsch       test_map_contains_expected_items(
318*c1757967SJacob Faibussowitsch         [&](std::vector<value_type> &key_value_pairs)
319*c1757967SJacob Faibussowitsch         {
320*c1757967SJacob Faibussowitsch           PetscFunctionBegin;
321*c1757967SJacob Faibussowitsch           for (auto &&key_value : key_value_pairs) {
322*c1757967SJacob Faibussowitsch             PetscCallCXX(map[key_value.first] = key_value.second);
323*c1757967SJacob Faibussowitsch             PetscCall(check_all_reinsert(key_value));
324*c1757967SJacob Faibussowitsch           }
325*c1757967SJacob Faibussowitsch           PetscFunctionReturn(0);
326*c1757967SJacob Faibussowitsch         },
327*c1757967SJacob Faibussowitsch         108
328*c1757967SJacob Faibussowitsch       )
329*c1757967SJacob Faibussowitsch     );
330*c1757967SJacob Faibussowitsch     // clang-format on
331*c1757967SJacob Faibussowitsch 
332*c1757967SJacob Faibussowitsch     // test that inserting using std algorithms work
333*c1757967SJacob Faibussowitsch     {
334*c1757967SJacob Faibussowitsch       value_type saved_value;
335*c1757967SJacob Faibussowitsch 
336*c1757967SJacob Faibussowitsch       // clang-format off
337*c1757967SJacob Faibussowitsch       PetscCall(
338*c1757967SJacob Faibussowitsch         test_map_contains_expected_items(
339*c1757967SJacob Faibussowitsch           [&](std::vector<value_type> &key_value_pairs)
340*c1757967SJacob Faibussowitsch           {
341*c1757967SJacob Faibussowitsch             PetscFunctionBegin;
342*c1757967SJacob Faibussowitsch             // save this for later
343*c1757967SJacob Faibussowitsch             PetscCallCXX(saved_value = key_value_pairs.front());
344*c1757967SJacob Faibussowitsch             // test the algorithm insert works as expected
345*c1757967SJacob Faibussowitsch             PetscCallCXX(std::copy(key_value_pairs.cbegin(), key_value_pairs.cend(), std::inserter(map, map.begin())));
346*c1757967SJacob Faibussowitsch             PetscFunctionReturn(0);
347*c1757967SJacob Faibussowitsch           },
348*c1757967SJacob Faibussowitsch           179
349*c1757967SJacob Faibussowitsch         )
350*c1757967SJacob Faibussowitsch       );
351*c1757967SJacob Faibussowitsch       // clang-format on
352*c1757967SJacob Faibussowitsch       auto it = map.find(saved_value.first);
353*c1757967SJacob Faibussowitsch 
354*c1757967SJacob Faibussowitsch       // can't use map[] since that might inadvertently insert it
355*c1757967SJacob Faibussowitsch       MapCheck(map, it != map.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map failed no longer contains key-value pair (%s -> %s) after std::copy() and container went out of scope", this->key_printer(saved_value.first), this->value_printer(saved_value.second));
356*c1757967SJacob Faibussowitsch       MapCheck(map, it->first == saved_value.first, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map founnd iterator key (%s) does not match expected key (%s) after std::copy() insertion", this->key_printer(it->first), this->key_printer(saved_value.first));
357*c1757967SJacob Faibussowitsch       MapCheck(map, it->second == saved_value.second, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map founnd iterator value (%s) does not match expected value (%s) after std::copy() insertion", this->value_printer(it->second), this->value_printer(saved_value.second));
358*c1757967SJacob Faibussowitsch     }
359*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
360*c1757967SJacob Faibussowitsch   }
361*c1757967SJacob Faibussowitsch 
362*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_insert() noexcept
363*c1757967SJacob Faibussowitsch   {
364*c1757967SJacob Faibussowitsch     map_type map;
365*c1757967SJacob Faibussowitsch 
366*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
367*c1757967SJacob Faibussowitsch     PetscCall(test_insert(map));
368*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
369*c1757967SJacob Faibussowitsch   }
370*c1757967SJacob Faibussowitsch 
371*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_find(map_type &map) noexcept
372*c1757967SJacob Faibussowitsch   {
373*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
374*c1757967SJacob Faibussowitsch     {
375*c1757967SJacob Faibussowitsch       const auto sample_values = this->make_key_values(145);
376*c1757967SJacob Faibussowitsch 
377*c1757967SJacob Faibussowitsch       map = map_type(sample_values.begin(), sample_values.end());
378*c1757967SJacob Faibussowitsch       for (auto &&kv : sample_values) {
379*c1757967SJacob Faibussowitsch         auto &&key   = kv.first;
380*c1757967SJacob Faibussowitsch         auto &&value = kv.second;
381*c1757967SJacob Faibussowitsch         auto   it    = map.find(key);
382*c1757967SJacob Faibussowitsch 
383*c1757967SJacob Faibussowitsch         MapCheck(map, it != map.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Failed to find %s in map", this->key_printer(key));
384*c1757967SJacob Faibussowitsch         MapCheck(map, it->first == key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Find iterator key %s != expected %s", this->key_printer(it->first), this->key_printer(key));
385*c1757967SJacob Faibussowitsch         MapCheck(map, it->second == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Find iterator value %s != expected %s", this->value_printer(it->second), this->value_printer(value));
386*c1757967SJacob Faibussowitsch         MapCheck(map, map.contains(key), PETSC_COMM_SELF, PETSC_ERR_PLIB, "map.contains(key) reports false, even though map.find(key) successfully found it! key: %s", this->key_printer(key));
387*c1757967SJacob Faibussowitsch         MapCheck(map, map.count(key) == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "map.count(%s) %zu != 1", this->key_printer(key), map.count(key));
388*c1757967SJacob Faibussowitsch 
389*c1757967SJacob Faibussowitsch         {
390*c1757967SJacob Faibussowitsch           const auto  range       = map.equal_range(key);
391*c1757967SJacob Faibussowitsch           const auto &range_begin = range.first;
392*c1757967SJacob Faibussowitsch           const auto  range_size  = std::distance(range_begin, range.second);
393*c1757967SJacob Faibussowitsch 
394*c1757967SJacob Faibussowitsch           MapCheck(map, range_size == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map equal_range() returned a range of size %zu != 1", range_size);
395*c1757967SJacob Faibussowitsch           MapCheck(map, range_begin->first == key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Equal range iterator key %s != expected %s", this->key_printer(range_begin->first), this->key_printer(key));
396*c1757967SJacob Faibussowitsch           MapCheck(map, range_begin->second == value, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Equal range iterator value %s != expected %s", this->value_printer(range_begin->second), this->value_printer(value));
397*c1757967SJacob Faibussowitsch         }
398*c1757967SJacob Faibussowitsch       }
399*c1757967SJacob Faibussowitsch     }
400*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
401*c1757967SJacob Faibussowitsch   }
402*c1757967SJacob Faibussowitsch 
403*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_find() noexcept
404*c1757967SJacob Faibussowitsch   {
405*c1757967SJacob Faibussowitsch     map_type map;
406*c1757967SJacob Faibussowitsch 
407*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
408*c1757967SJacob Faibussowitsch     PetscCall(test_find(map));
409*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
410*c1757967SJacob Faibussowitsch   }
411*c1757967SJacob Faibussowitsch 
412*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_erase(map_type &map) noexcept
413*c1757967SJacob Faibussowitsch   {
414*c1757967SJacob Faibussowitsch     auto           sample_values = this->make_key_values(57);
415*c1757967SJacob Faibussowitsch     const map_type backup(sample_values.cbegin(), sample_values.cend());
416*c1757967SJacob Faibussowitsch     const auto     check_map_is_truly_empty = [&](map_type &map) {
417*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
418*c1757967SJacob Faibussowitsch       MapCheck(map, map.size() == 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing map via iterator range didn't work, map has size %zu", map.size());
419*c1757967SJacob Faibussowitsch       MapCheck(map, map.empty(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing map via iterators didn't work, map is not empty, has size %zu", map.size());
420*c1757967SJacob Faibussowitsch       // this loop should never actually fire!
421*c1757967SJacob Faibussowitsch       for (auto it = map.begin(); it != map.end(); ++it) MapCheck(map, false, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing via iterator range did not work, map.begin() != map.end()%s", "");
422*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
423*c1757967SJacob Faibussowitsch     };
424*c1757967SJacob Faibussowitsch 
425*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
426*c1757967SJacob Faibussowitsch     PetscCallCXX(map = backup);
427*c1757967SJacob Faibussowitsch     // test single erase from iterator works
428*c1757967SJacob Faibussowitsch     {
429*c1757967SJacob Faibussowitsch       const auto it        = map.begin();
430*c1757967SJacob Faibussowitsch       const auto begin_key = it->first;
431*c1757967SJacob Faibussowitsch       const auto begin_val = it->second;
432*c1757967SJacob Faibussowitsch 
433*c1757967SJacob Faibussowitsch       PetscCallCXX(map.erase(it));
434*c1757967SJacob Faibussowitsch       for (auto &&kv : map) MapCheck(map, kv.first != begin_key, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Erasing %s did not work, found again in map", this->key_printer(begin_key));
435*c1757967SJacob Faibussowitsch       // reinsert the value
436*c1757967SJacob Faibussowitsch       PetscCallCXX(map[begin_key] = begin_val);
437*c1757967SJacob Faibussowitsch     }
438*c1757967SJacob Faibussowitsch 
439*c1757967SJacob Faibussowitsch     // test erase from iterator
440*c1757967SJacob Faibussowitsch     for (auto it = map.begin(); it != map.end(); ++it) {
441*c1757967SJacob Faibussowitsch       const auto before = it;
442*c1757967SJacob Faibussowitsch 
443*c1757967SJacob Faibussowitsch       PetscCallCXX(map.erase(it));
444*c1757967SJacob Faibussowitsch       MapCheck(map, before == it, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Iterator changed during erase%s", "");
445*c1757967SJacob Faibussowitsch       MapCheck(map, map.occupied(before) == false, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Iterator (%s -> %s) occupied after erase", this->key_printer(before->first), this->value_printer(before->second));
446*c1757967SJacob Faibussowitsch     }
447*c1757967SJacob Faibussowitsch 
448*c1757967SJacob Faibussowitsch     // test erase from iterator range
449*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
450*c1757967SJacob Faibussowitsch     PetscCallCXX(map = backup);
451*c1757967SJacob Faibussowitsch     PetscCallCXX(map.erase(map.begin(), map.end()));
452*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
453*c1757967SJacob Faibussowitsch 
454*c1757967SJacob Faibussowitsch     // test erase by clear
455*c1757967SJacob Faibussowitsch     PetscCallCXX(map = backup);
456*c1757967SJacob Faibussowitsch     PetscCall(map.clear());
457*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
458*c1757967SJacob Faibussowitsch 
459*c1757967SJacob Faibussowitsch     // test that clear works OK (used to be a bug when inserting after clear)
460*c1757967SJacob Faibussowitsch     PetscCallCXX(map.insert(generator()));
461*c1757967SJacob Faibussowitsch     PetscCallCXX(map.insert(generator()));
462*c1757967SJacob Faibussowitsch     PetscCallCXX(map.insert(generator()));
463*c1757967SJacob Faibussowitsch     PetscCallCXX(map.insert(generator()));
464*c1757967SJacob Faibussowitsch     PetscCallCXX(map.erase(map.begin(), map.end()));
465*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
466*c1757967SJacob Faibussowitsch 
467*c1757967SJacob Faibussowitsch     // test erase by member function swapping with empty map
468*c1757967SJacob Faibussowitsch     for (auto &&kv : sample_values) PetscCallCXX(map.emplace(kv.first, kv.second));
469*c1757967SJacob Faibussowitsch     {
470*c1757967SJacob Faibussowitsch       map_type alt;
471*c1757967SJacob Faibussowitsch 
472*c1757967SJacob Faibussowitsch       // has the effect of clearing the map
473*c1757967SJacob Faibussowitsch       PetscCallCXX(map.swap(alt));
474*c1757967SJacob Faibussowitsch     }
475*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
476*c1757967SJacob Faibussowitsch 
477*c1757967SJacob Faibussowitsch     // test erase by std::swap with empty map
478*c1757967SJacob Faibussowitsch     PetscCallCXX(map = backup);
479*c1757967SJacob Faibussowitsch     {
480*c1757967SJacob Faibussowitsch       using std::swap;
481*c1757967SJacob Faibussowitsch       map_type alt;
482*c1757967SJacob Faibussowitsch 
483*c1757967SJacob Faibussowitsch       // has the effect of clearing the map
484*c1757967SJacob Faibussowitsch       PetscCallCXX(swap(map, alt));
485*c1757967SJacob Faibussowitsch     }
486*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
487*c1757967SJacob Faibussowitsch 
488*c1757967SJacob Faibussowitsch     // test erase by key, use new values to change it up
489*c1757967SJacob Faibussowitsch     sample_values = this->make_key_values();
490*c1757967SJacob Faibussowitsch     std::copy(sample_values.cbegin(), sample_values.cend(), std::inserter(map, map.begin()));
491*c1757967SJacob Faibussowitsch     for (auto &&kv : sample_values) PetscCallCXX(map.erase(kv.first));
492*c1757967SJacob Faibussowitsch     PetscCall(check_map_is_truly_empty(map));
493*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
494*c1757967SJacob Faibussowitsch   }
495*c1757967SJacob Faibussowitsch 
496*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_erase() noexcept
497*c1757967SJacob Faibussowitsch   {
498*c1757967SJacob Faibussowitsch     map_type map;
499*c1757967SJacob Faibussowitsch 
500*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
501*c1757967SJacob Faibussowitsch     PetscCall(test_erase(map));
502*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
503*c1757967SJacob Faibussowitsch   }
504*c1757967SJacob Faibussowitsch 
505*c1757967SJacob Faibussowitsch   // stupid dummy function because auto-lambdas are C++14
506*c1757967SJacob Faibussowitsch   template <typename It>
507*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_iterators(map_type &map, It it, It it2) noexcept
508*c1757967SJacob Faibussowitsch   {
509*c1757967SJacob Faibussowitsch     constexpr std::size_t max_iter  = 10000;
510*c1757967SJacob Faibussowitsch     constexpr auto        is_normal = std::is_same<It, typename map_type::iterator>::value;
511*c1757967SJacob Faibussowitsch     constexpr auto        is_const  = std::is_same<It, typename map_type::const_iterator>::value;
512*c1757967SJacob Faibussowitsch     static_assert(is_normal || is_const, "");
513*c1757967SJacob Faibussowitsch     constexpr const char *it_name = is_normal ? "Non-const" : "Const";
514*c1757967SJacob Faibussowitsch 
515*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
516*c1757967SJacob Faibussowitsch     MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself?", it_name);
517*c1757967SJacob Faibussowitsch     PetscCallCXX(++it);
518*c1757967SJacob Faibussowitsch     PetscCallCXX(it2++);
519*c1757967SJacob Faibussowitsch     MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself after ++it, and it2++", it_name);
520*c1757967SJacob Faibussowitsch     PetscCallCXX(--it);
521*c1757967SJacob Faibussowitsch     PetscCallCXX(it2--);
522*c1757967SJacob Faibussowitsch     MapCheck(map, it == it2, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator does not equal itself after --it, and it2--", it_name);
523*c1757967SJacob Faibussowitsch     MapCheck(map, map.size() < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Forward progress test only works properly if the map size (%zu) < %zu", map.size(), max_iter);
524*c1757967SJacob Faibussowitsch     // check that the prefix and postfix increment and decerement make forward progress
525*c1757967SJacob Faibussowitsch     {
526*c1757967SJacob Faibussowitsch       std::size_t i;
527*c1757967SJacob Faibussowitsch 
528*c1757967SJacob Faibussowitsch       // increment
529*c1757967SJacob Faibussowitsch       PetscCallCXX(it = map.begin());
530*c1757967SJacob Faibussowitsch       for (i = 0; i < max_iter; ++i) {
531*c1757967SJacob Faibussowitsch         if (it == map.end()) break;
532*c1757967SJacob Faibussowitsch         PetscCallCXX(++it);
533*c1757967SJacob Faibussowitsch       }
534*c1757967SJacob Faibussowitsch       MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using prefix increment! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size());
535*c1757967SJacob Faibussowitsch       PetscCallCXX(it = map.begin());
536*c1757967SJacob Faibussowitsch       for (i = 0; i < max_iter; ++i) {
537*c1757967SJacob Faibussowitsch         if (it == map.end()) break;
538*c1757967SJacob Faibussowitsch         PetscCallCXX(it++);
539*c1757967SJacob Faibussowitsch       }
540*c1757967SJacob Faibussowitsch       MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using postfix increment! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size());
541*c1757967SJacob Faibussowitsch 
542*c1757967SJacob Faibussowitsch       // decrement
543*c1757967SJacob Faibussowitsch       PetscCallCXX(it = std::prev(map.end()));
544*c1757967SJacob Faibussowitsch       for (i = 0; i < max_iter; ++i) {
545*c1757967SJacob Faibussowitsch         if (it == map.begin()) break;
546*c1757967SJacob Faibussowitsch         PetscCallCXX(--it);
547*c1757967SJacob Faibussowitsch       }
548*c1757967SJacob Faibussowitsch       MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using prefix decrement! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size());
549*c1757967SJacob Faibussowitsch       PetscCallCXX(it = std::prev(map.end()));
550*c1757967SJacob Faibussowitsch       for (i = 0; i < max_iter; ++i) {
551*c1757967SJacob Faibussowitsch         if (it == map.begin()) break;
552*c1757967SJacob Faibussowitsch         PetscCallCXX(it--);
553*c1757967SJacob Faibussowitsch       }
554*c1757967SJacob Faibussowitsch       MapCheck(map, i < max_iter, PETSC_COMM_SELF, PETSC_ERR_PLIB, "%s iterator did not appear to make forward progress using postfix decrement! Reached maximum iteration count %zu for map of size %zu", it_name, max_iter, map.size());
555*c1757967SJacob Faibussowitsch     }
556*c1757967SJacob Faibussowitsch 
557*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
558*c1757967SJacob Faibussowitsch   }
559*c1757967SJacob Faibussowitsch 
560*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test_misc() noexcept
561*c1757967SJacob Faibussowitsch   {
562*c1757967SJacob Faibussowitsch     const auto sample_values = this->make_key_values(97);
563*c1757967SJacob Faibussowitsch     map_type   map(sample_values.begin(), sample_values.end());
564*c1757967SJacob Faibussowitsch 
565*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
566*c1757967SJacob Faibussowitsch     PetscCall(this->test_iterators(map, map.begin(), map.begin()));
567*c1757967SJacob Faibussowitsch     PetscCall(this->test_iterators(map, map.cbegin(), map.cbegin()));
568*c1757967SJacob Faibussowitsch     {
569*c1757967SJacob Faibussowitsch       const auto backup                            = map;
570*c1757967SJacob Faibussowitsch       auto       map_copy                          = map;
571*c1757967SJacob Faibussowitsch       const auto check_original_map_did_not_change = [&](const char op[]) {
572*c1757967SJacob Faibussowitsch         PetscFunctionBegin;
573*c1757967SJacob Faibussowitsch         // the original map should not have changed at all
574*c1757967SJacob Faibussowitsch         MapCheck(map, map == backup, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Map does not equal the original map after %s", op);
575*c1757967SJacob Faibussowitsch         PetscFunctionReturn(0);
576*c1757967SJacob Faibussowitsch       };
577*c1757967SJacob Faibussowitsch 
578*c1757967SJacob Faibussowitsch       MapCheck(map_copy, map == map_copy, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Copy of map does not equal the original map%s", "");
579*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("move assign"));
580*c1757967SJacob Faibussowitsch       // test that the copied map works OK
581*c1757967SJacob Faibussowitsch       PetscCall(this->test_insert(map_copy));
582*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("test_insert()"));
583*c1757967SJacob Faibussowitsch       PetscCall(this->test_find(map_copy));
584*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("test_find()"));
585*c1757967SJacob Faibussowitsch       PetscCall(this->test_erase(map_copy));
586*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("test_erase()"));
587*c1757967SJacob Faibussowitsch       PetscCallCXX(map_copy = map);
588*c1757967SJacob Faibussowitsch 
589*c1757967SJacob Faibussowitsch       auto moved_copy = std::move(map_copy);
590*c1757967SJacob Faibussowitsch 
591*c1757967SJacob Faibussowitsch       MapCheck(moved_copy, map == moved_copy, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Moved copy of map does not equal the original map%s", "");
592*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("move assign"));
593*c1757967SJacob Faibussowitsch       PetscCall(this->test_insert(moved_copy));
594*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("test_insert()"));
595*c1757967SJacob Faibussowitsch       PetscCall(this->test_find(moved_copy));
596*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("test_find()"));
597*c1757967SJacob Faibussowitsch       PetscCall(this->test_erase(moved_copy));
598*c1757967SJacob Faibussowitsch       PetscCall(check_original_map_did_not_change("test_erase()"));
599*c1757967SJacob Faibussowitsch     }
600*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
601*c1757967SJacob Faibussowitsch   }
602*c1757967SJacob Faibussowitsch 
603*c1757967SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode test() noexcept
604*c1757967SJacob Faibussowitsch   {
605*c1757967SJacob Faibussowitsch     PetscFunctionBegin;
606*c1757967SJacob Faibussowitsch     PetscCall(this->test_insert());
607*c1757967SJacob Faibussowitsch     PetscCall(this->test_find());
608*c1757967SJacob Faibussowitsch     PetscCall(this->test_erase());
609*c1757967SJacob Faibussowitsch     PetscCall(this->test_misc());
610*c1757967SJacob Faibussowitsch     PetscFunctionReturn(0);
611*c1757967SJacob Faibussowitsch   }
612*c1757967SJacob Faibussowitsch 
613*c1757967SJacob Faibussowitsch private:
614*c1757967SJacob Faibussowitsch   PETSC_NODISCARD std::vector<value_type> make_key_values(std::size_t size = 100) const noexcept
615*c1757967SJacob Faibussowitsch   {
616*c1757967SJacob Faibussowitsch     std::vector<value_type> v(size);
617*c1757967SJacob Faibussowitsch 
618*c1757967SJacob Faibussowitsch     std::generate(v.begin(), v.end(), this->generator);
619*c1757967SJacob Faibussowitsch     return v;
620*c1757967SJacob Faibussowitsch   }
621*c1757967SJacob Faibussowitsch };
622*c1757967SJacob Faibussowitsch #ifdef PETSC_GCC_LAMBDA_VISIBILITY_WORKAROUND
623*c1757967SJacob Faibussowitsch   #pragma GCC visibility pop
624*c1757967SJacob Faibussowitsch #endif
625*c1757967SJacob Faibussowitsch 
626*c1757967SJacob Faibussowitsch template <typename... T, typename... Args>
627*c1757967SJacob Faibussowitsch PETSC_NODISCARD static MapTester<T...> make_tester(PetscViewer vwr, const char name[], Args &&...args)
628*c1757967SJacob Faibussowitsch {
629*c1757967SJacob Faibussowitsch   return {vwr, name, std::forward<Args>(args)...};
630*c1757967SJacob Faibussowitsch }
631*c1757967SJacob Faibussowitsch 
632*c1757967SJacob Faibussowitsch int main(int argc, char *argv[])
633*c1757967SJacob Faibussowitsch {
634*c1757967SJacob Faibussowitsch   PetscViewer vwr;
635*c1757967SJacob Faibussowitsch   PetscRandom rand;
636*c1757967SJacob Faibussowitsch 
637*c1757967SJacob Faibussowitsch   PetscFunctionBeginUser;
638*c1757967SJacob Faibussowitsch   PetscCall(PetscInitialize(&argc, &argv, nullptr, help));
639*c1757967SJacob Faibussowitsch   PetscCall(PetscRandomCreate(PETSC_COMM_SELF, &rand));
640*c1757967SJacob Faibussowitsch   PetscCall(PetscRandomSetInterval(rand, INT_MIN, INT_MAX));
641*c1757967SJacob Faibussowitsch   PetscCall(PetscRandomSetFromOptions(rand));
642*c1757967SJacob Faibussowitsch   PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr));
643*c1757967SJacob Faibussowitsch 
644*c1757967SJacob Faibussowitsch   {
645*c1757967SJacob Faibussowitsch     // printer functions
646*c1757967SJacob Faibussowitsch     const auto int_printer = [](int key, std::string &buf) {
647*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
648*c1757967SJacob Faibussowitsch       PetscCallCXX(buf = std::to_string(key));
649*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
650*c1757967SJacob Faibussowitsch     };
651*c1757967SJacob Faibussowitsch     const auto double_printer = [](double value, std::string &buf) {
652*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
653*c1757967SJacob Faibussowitsch       PetscCallCXX(buf = std::to_string(value));
654*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
655*c1757967SJacob Faibussowitsch     };
656*c1757967SJacob Faibussowitsch     const auto foo_printer = [](const Foo &key, std::string &buf) {
657*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
658*c1757967SJacob Faibussowitsch       PetscCall(key.to_string(buf));
659*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
660*c1757967SJacob Faibussowitsch     };
661*c1757967SJacob Faibussowitsch     const auto bar_printer = [](const Bar &value, std::string &buf) {
662*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
663*c1757967SJacob Faibussowitsch       PetscCall(value.to_string(buf));
664*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
665*c1757967SJacob Faibussowitsch     };
666*c1757967SJacob Faibussowitsch     const auto pair_printer = [](const std::pair<int, double> &value, std::string &buf) {
667*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
668*c1757967SJacob Faibussowitsch       PetscCallCXX(buf = '<' + std::to_string(value.first) + ", " + std::to_string(value.second) + '>');
669*c1757967SJacob Faibussowitsch       PetscFunctionReturn(0);
670*c1757967SJacob Faibussowitsch     };
671*c1757967SJacob Faibussowitsch 
672*c1757967SJacob Faibussowitsch     // generator functions
673*c1757967SJacob Faibussowitsch     const auto make_int = [&] {
674*c1757967SJacob Faibussowitsch       PetscReal x = 0.;
675*c1757967SJacob Faibussowitsch 
676*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
677*c1757967SJacob Faibussowitsch       PetscCallAbort(PETSC_COMM_SELF, PetscRandomGetValueReal(rand, &x));
678*c1757967SJacob Faibussowitsch       PetscFunctionReturn(static_cast<int>(x));
679*c1757967SJacob Faibussowitsch     };
680*c1757967SJacob Faibussowitsch     const auto make_double = [&] {
681*c1757967SJacob Faibussowitsch       PetscReal x = 0.;
682*c1757967SJacob Faibussowitsch 
683*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
684*c1757967SJacob Faibussowitsch       PetscCallAbort(PETSC_COMM_SELF, PetscRandomGetValueReal(rand, &x));
685*c1757967SJacob Faibussowitsch       PetscFunctionReturn(static_cast<double>(x));
686*c1757967SJacob Faibussowitsch     };
687*c1757967SJacob Faibussowitsch     const auto make_foo = [&] {
688*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
689*c1757967SJacob Faibussowitsch       auto ret = Foo{make_int(), make_double()};
690*c1757967SJacob Faibussowitsch       PetscFunctionReturn(ret);
691*c1757967SJacob Faibussowitsch     };
692*c1757967SJacob Faibussowitsch     const auto make_bar = [&] {
693*c1757967SJacob Faibussowitsch       constexpr std::size_t max_size = 14, min_size = 1;
694*c1757967SJacob Faibussowitsch       const auto            isize = std::abs(make_int());
695*c1757967SJacob Faibussowitsch       std::vector<int>      x(std::max(static_cast<std::size_t>(isize) % max_size, min_size));
696*c1757967SJacob Faibussowitsch 
697*c1757967SJacob Faibussowitsch       PetscFunctionBegin;
698*c1757967SJacob Faibussowitsch       PetscCallCXXAbort(PETSC_COMM_SELF, std::generate(x.begin(), x.end(), make_int));
699*c1757967SJacob Faibussowitsch       auto ret = Bar{std::move(x), std::to_string(isize)};
700*c1757967SJacob Faibussowitsch       PetscFunctionReturn(ret);
701*c1757967SJacob Faibussowitsch     };
702*c1757967SJacob Faibussowitsch 
703*c1757967SJacob Faibussowitsch     const auto int_double_generator = [&] { return std::make_pair(make_int(), make_double()); };
704*c1757967SJacob Faibussowitsch     PetscCall(make_tester<int, double>(vwr, "int-double basic map", int_printer, double_printer, int_double_generator).test());
705*c1757967SJacob Faibussowitsch     PetscCall(make_tester<int, double, BadHash>(vwr, "int-double bad hash map", int_printer, double_printer, int_double_generator).test());
706*c1757967SJacob Faibussowitsch 
707*c1757967SJacob Faibussowitsch     const auto int_foo_generator = [&] { return std::make_pair(make_int(), make_foo()); };
708*c1757967SJacob Faibussowitsch     PetscCall(make_tester<int, Foo, BadHash>(vwr, "int-foo bad hash map", int_printer, foo_printer, int_foo_generator).test());
709*c1757967SJacob Faibussowitsch 
710*c1757967SJacob Faibussowitsch     const auto foo_bar_generator = [&] { return std::make_pair(make_foo(), make_bar()); };
711*c1757967SJacob Faibussowitsch     PetscCall(make_tester<Foo, Bar>(vwr, "foo-bar basic map", foo_printer, bar_printer, foo_bar_generator).test());
712*c1757967SJacob Faibussowitsch     PetscCall(make_tester<Foo, Bar, BadHash>(vwr, "foo-bar bad hash map", foo_printer, bar_printer, foo_bar_generator).test());
713*c1757967SJacob Faibussowitsch 
714*c1757967SJacob Faibussowitsch     // these test that the indirect_hasher and indirect_equals classes don't barf, since the
715*c1757967SJacob Faibussowitsch     // value_type of the map and hashers is both the same thing
716*c1757967SJacob Faibussowitsch     const auto pair_pair_generator = [&] {
717*c1757967SJacob Faibussowitsch       auto pair = std::make_pair(make_int(), make_double());
718*c1757967SJacob Faibussowitsch       return std::make_pair(pair, pair);
719*c1757967SJacob Faibussowitsch     };
720*c1757967SJacob Faibussowitsch     PetscCall(make_tester<std::pair<int, double>, std::pair<int, double>>(vwr, "pair<int, double>-pair<int, double> basic map", pair_printer, pair_printer, pair_pair_generator).test());
721*c1757967SJacob Faibussowitsch     PetscCall(make_tester<std::pair<int, double>, std::pair<int, double>, BadHash>(vwr, "pair<int, double>-pair<int, double> bad hash map", pair_printer, pair_printer, pair_pair_generator).test());
722*c1757967SJacob Faibussowitsch   }
723*c1757967SJacob Faibussowitsch 
724*c1757967SJacob Faibussowitsch   PetscCall(PetscRandomDestroy(&rand));
725*c1757967SJacob Faibussowitsch   PetscCall(PetscFinalize());
726*c1757967SJacob Faibussowitsch   return 0;
727*c1757967SJacob Faibussowitsch }
728*c1757967SJacob Faibussowitsch 
729*c1757967SJacob Faibussowitsch /*TEST
730*c1757967SJacob Faibussowitsch 
731*c1757967SJacob Faibussowitsch   test:
732*c1757967SJacob Faibussowitsch     output_file: ./output/empty.out
733*c1757967SJacob Faibussowitsch 
734*c1757967SJacob Faibussowitsch TEST*/
735