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