about summary refs log tree commit diff
path: root/test/map/generic.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'test/map/generic.ipp')
-rw-r--r--test/map/generic.ipp312
1 files changed, 312 insertions, 0 deletions
diff --git a/test/map/generic.ipp b/test/map/generic.ipp
new file mode 100644
index 000000000000..18fc9d11f567
--- /dev/null
+++ b/test/map/generic.ipp
@@ -0,0 +1,312 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#ifndef MAP_T
+#error "define the map template to use in MAP_T"
+#include <immer/map.hpp>
+#define MAP_T ::immer::map
+#endif
+
+#include <immer/algorithm.hpp>
+
+#include "test/dada.hpp"
+#include "test/util.hpp"
+
+#include <catch.hpp>
+
+#include <random>
+#include <unordered_set>
+
+template <typename T = unsigned>
+auto make_generator()
+{
+    auto engine = std::default_random_engine{42};
+    auto dist   = std::uniform_int_distribution<T>{};
+    return std::bind(dist, engine);
+}
+
+struct conflictor
+{
+    unsigned v1;
+    unsigned v2;
+
+    bool operator==(const conflictor& x) const
+    {
+        return v1 == x.v1 && v2 == x.v2;
+    }
+};
+
+struct hash_conflictor
+{
+    std::size_t operator()(const conflictor& x) const { return x.v1; }
+};
+
+auto make_values_with_collisions(unsigned n)
+{
+    auto gen   = make_generator();
+    auto vals  = std::vector<std::pair<conflictor, unsigned>>{};
+    auto vals_ = std::unordered_set<conflictor, hash_conflictor>{};
+    auto i     = 0u;
+    generate_n(back_inserter(vals), n, [&] {
+        auto newv = conflictor{};
+        do {
+            newv = {unsigned(gen() % (n / 2)), gen()};
+        } while (!vals_.insert(newv).second);
+        return std::pair<conflictor, unsigned>{newv, i++};
+    });
+    return vals;
+}
+
+auto make_test_map(unsigned n)
+{
+    auto s = MAP_T<unsigned, unsigned>{};
+    for (auto i = 0u; i < n; ++i)
+        s = s.insert({i, i});
+    return s;
+}
+
+auto make_test_map(const std::vector<std::pair<conflictor, unsigned>>& vals)
+{
+    auto s = MAP_T<conflictor, unsigned, hash_conflictor>{};
+    for (auto&& v : vals)
+        s = s.insert(v);
+    return s;
+}
+
+TEST_CASE("instantiation")
+{
+    SECTION("default")
+    {
+        auto v = MAP_T<int, int>{};
+        CHECK(v.size() == 0u);
+    }
+}
+
+TEST_CASE("basic insertion")
+{
+    auto v1 = MAP_T<int, int>{};
+    CHECK(v1.count(42) == 0);
+
+    auto v2 = v1.insert({42, {}});
+    CHECK(v1.count(42) == 0);
+    CHECK(v2.count(42) == 1);
+
+    auto v3 = v2.insert({42, {}});
+    CHECK(v1.count(42) == 0);
+    CHECK(v2.count(42) == 1);
+    CHECK(v3.count(42) == 1);
+}
+
+TEST_CASE("accessor")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+    CHECK(v[0] == 0);
+    CHECK(v[42] == 42);
+    CHECK(v[665] == 665);
+    CHECK(v[666] == 0);
+    CHECK(v[1234] == 0);
+}
+
+TEST_CASE("at")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+    CHECK(v.at(0) == 0);
+    CHECK(v.at(42) == 42);
+    CHECK(v.at(665) == 665);
+    CHECK_THROWS_AS(v.at(666), std::out_of_range&);
+    CHECK_THROWS_AS(v.at(1234), std::out_of_range&);
+}
+
+TEST_CASE("find")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+    CHECK(*v.find(0) == 0);
+    CHECK(*v.find(42) == 42);
+    CHECK(*v.find(665) == 665);
+    CHECK(v.find(666) == nullptr);
+    CHECK(v.find(1234) == nullptr);
+}
+
+TEST_CASE("equals and setting")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+
+    CHECK(v == v);
+    CHECK(v != v.insert({1234, 42}));
+    CHECK(v != v.erase(32));
+    CHECK(v == v.insert({1234, 42}).erase(1234));
+    CHECK(v == v.erase(32).insert({32, 32}));
+
+    CHECK(v.set(1234, 42) == v.insert({1234, 42}));
+    CHECK(v.update(1234, [](auto&& x) { return x + 1; }) == v.set(1234, 1));
+    CHECK(v.update(42, [](auto&& x) { return x + 1; }) == v.set(42, 43));
+}
+
+TEST_CASE("iterator")
+{
+    const auto N = 666u;
+    auto v       = make_test_map(N);
+
+    SECTION("empty set")
+    {
+        auto s = MAP_T<unsigned, unsigned>{};
+        CHECK(s.begin() == s.end());
+    }
+
+    SECTION("works with range loop")
+    {
+        auto seen = std::unordered_set<unsigned>{};
+        for (const auto& x : v)
+            CHECK(seen.insert(x.first).second);
+        CHECK(seen.size() == v.size());
+    }
+
+    SECTION("iterator and collisions")
+    {
+        auto vals = make_values_with_collisions(N);
+        auto s    = make_test_map(vals);
+        auto seen = std::unordered_set<conflictor, hash_conflictor>{};
+        for (const auto& x : s)
+            CHECK(seen.insert(x.first).second);
+        CHECK(seen.size() == s.size());
+    }
+}
+
+TEST_CASE("accumulate")
+{
+    const auto n = 666u;
+    auto v       = make_test_map(n);
+
+    auto expected_n = [](auto n) { return n * (n - 1) / 2; };
+
+    SECTION("sum collection")
+    {
+        auto acc = [](unsigned acc, const std::pair<unsigned, unsigned>& x) {
+            return acc + x.first + x.second;
+        };
+        auto sum = immer::accumulate(v, 0u, acc);
+        CHECK(sum == 2 * expected_n(v.size()));
+    }
+
+    SECTION("sum collisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto s    = make_test_map(vals);
+        auto acc  = [](unsigned r, std::pair<conflictor, unsigned> x) {
+            return r + x.first.v1 + x.first.v2 + x.second;
+        };
+        auto sum1 = std::accumulate(vals.begin(), vals.end(), 0u, acc);
+        auto sum2 = immer::accumulate(s, 0u, acc);
+        CHECK(sum1 == sum2);
+    }
+}
+
+TEST_CASE("update a lot")
+{
+    auto v = make_test_map(666u);
+
+    for (decltype(v.size()) i = 0; i < v.size(); ++i) {
+        v = v.update(i, [](auto&& x) { return x + 1; });
+        CHECK(v[i] == i + 1);
+    }
+}
+
+TEST_CASE("exception safety")
+{
+    constexpr auto n = 2666u;
+
+    using dadaist_map_t =
+        typename dadaist_wrapper<MAP_T<unsigned, unsigned>>::type;
+    using dadaist_conflictor_map_t = typename dadaist_wrapper<
+        MAP_T<conflictor, unsigned, hash_conflictor>>::type;
+
+    SECTION("update collisions")
+    {
+        auto v = dadaist_map_t{};
+        auto d = dadaism{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.set(i, i);
+        for (auto i = 0u; i < v.size();) {
+            try {
+                auto s = d.next();
+                v      = v.update(i, [](auto x) { return x + 1; });
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.at(i) == i + 1);
+            for (auto i : test_irange(i, n))
+                CHECK(v.at(i) == i);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+
+    SECTION("update collisisions")
+    {
+        auto vals = make_values_with_collisions(n);
+        auto v    = dadaist_conflictor_map_t{};
+        auto d    = dadaism{};
+        for (auto i = 0u; i < n; ++i)
+            v = v.insert(vals[i]);
+        for (auto i = 0u; i < v.size();) {
+            try {
+                auto s = d.next();
+                v      = v.update(vals[i].first, [](auto x) { return x + 1; });
+                ++i;
+            } catch (dada_error) {}
+            for (auto i : test_irange(0u, i))
+                CHECK(v.at(vals[i].first) == vals[i].second + 1);
+            for (auto i : test_irange(i, n))
+                CHECK(v.at(vals[i].first) == vals[i].second);
+        }
+        CHECK(d.happenings > 0);
+        IMMER_TRACE_E(d.happenings);
+    }
+}
+
+namespace {
+
+class KElem
+{
+public:
+    KElem(int* elem) { this->elem = elem; }
+
+    bool operator==(const KElem& other) const
+    {
+        return this->elem == other.elem;
+    }
+
+    bool operator!=(const KElem& other) const { return !(*this == other); }
+
+    int* elem;
+};
+
+struct HashBlock
+{
+    size_t operator()(const KElem& block) const noexcept
+    {
+        return (uintptr_t) block.elem & 0xffffffff00000000;
+    }
+};
+
+using map = immer::map<KElem, KElem, HashBlock, std::equal_to<KElem>>;
+
+TEST_CASE("issue 134")
+{
+    int a[100];
+    map m;
+    for (int i = 0; i < 100; i++) {
+        m = m.set(KElem(a + i), KElem(a + i));
+    }
+}
+
+} // namespace