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