diff --git a/src/include/interval-map/interval-map.hh b/src/include/interval-map/interval-map.hh index ab3fb44..ce1d9dd 100644 --- a/src/include/interval-map/interval-map.hh +++ b/src/include/interval-map/interval-map.hh @@ -2,9 +2,6 @@ #include -// Testing class forward declaration -class IntervalMapTest; - namespace amby { template class interval_map { @@ -12,48 +9,17 @@ public: interval_map(V const& init) : init_(init) {} void assign(K const& begin, K const& end, V const& val) { - if (!(begin < end)) - return; - - auto it = underlying_.upper_bound(end); - auto const end_val = at_upper_bound(it); - - bool insert_begin = !(val == init_); - - while (it != underlying_.begin()) { - it = std::prev(it); - auto begin_found = it->first < begin; - if (begin_found) { - insert_begin = !(val == it->second); - break; - } - if (it != underlying_.end()) - // Account for up-coming `std::prev` with `++` - underlying_.erase(it++); - } - - if (insert_begin) - it = underlying_.insert(it, {begin, val}); - // Get the proper upper-bound for `end` - it = (it == underlying_.end()) ? it : std::next(it); - if (!(at_upper_bound(it) == end_val)) - underlying_.insert(it, {end, end_val}); + // TODO: implement } V const& operator[](K const& key) const { - return at_upper_bound(underlying_.upper_bound(key)); - } - - // Used in testing - friend class ::IntervalMapTest; - -private: - V const& at_upper_bound(std::map::const_iterator it) const { + auto it = underlying_.upper_bound(key); if (it == underlying_.begin()) return init_; return std::prev(it)->second; } +private: V init_; std::map underlying_{}; }; diff --git a/tests/unit/model.hh b/tests/unit/model.hh deleted file mode 100644 index 9acb19f..0000000 --- a/tests/unit/model.hh +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -template struct Range { - K begin; - K end; - V val; -}; - -// Same behaviour as interval_map, but implementation is trivally correct -template class Model { -public: - Model(V const& init) : init_(init) {} - - void assign(K const& begin, K const& end, V const& val) { - if (!(begin < end)) - return; - ranges_.emplace_back(begin, end, val); - } - - V const& operator[](K const& key) const { - for (auto it = ranges_.rbegin(); it != ranges_.rend(); ++it) { - if (key < it->begin) - continue; - if (it->end <= key) - continue; - return it->val; - } - return init_; - } - - V init_; - std::vector> ranges_{}; -}; diff --git a/tests/unit/unit_test.cc b/tests/unit/unit_test.cc index c429d29..288c811 100644 --- a/tests/unit/unit_test.cc +++ b/tests/unit/unit_test.cc @@ -2,12 +2,8 @@ #include -#include -#include #include -#include "model.hh" - template class KeyInterface { public: explicit KeyInterface(T val) : underlying_(val) {} @@ -62,82 +58,7 @@ static_assert(std::is_same_v< ValueInterface, decltype(std::numeric_limits>::lowest())>); -class IntervalMapTest : public testing::Test { -protected: - using key_type = char; - using value_type = int; - using map_type = amby::interval_map; - using model_type = Model; - - map_type map{0}; - model_type model{0}; - - void SetUp() override { - map = map_type{0}; - model = model_type{0}; - } - - void TearDown() override { - check(); - } - - void assign(key_type const& begin, key_type const& end, - value_type const& val) { - map.assign(begin, end, val); - model.assign(begin, end, val); - } - - void check() const { - SCOPED_TRACE(stringify_map()); - SCOPED_TRACE(stringify_operations()); - check_ranges(); - check_canonicity(); - } - - std::string stringify_map() const { - std::ostringstream out; - out << "map: "; - for (const auto& [key, val] : map.underlying_) - out << "[" << +key << ": " << +val << "]"; - return out.str(); - } - - std::string stringify_operations() const { - std::ostringstream out; - out << "ops: "; - for (const auto& [start, end, val] : model.ranges_) - out << "[" << +start << ":" << +end << " => " << +val << "]"; - return out.str(); - } - - // Compare against the fake 'Model' implementation - void check_ranges() const { - auto i = std::numeric_limits::min(); - for (; i < std::numeric_limits::max(); ++i) { - ASSERT_EQ(map[i], model[i]) << "(i: " << +i << ")"; - } - ASSERT_EQ(map[i], model[i]) << "(i: " << +i << ")"; - }; - - void check_canonicity() const { - // Consecutive map entries must not contain the same value - for (auto it = map.underlying_.begin(); it != map.underlying_.end(); - ++it) { - const auto next = std::next(it, 1); - if (next == map.underlying_.end()) - break; - EXPECT_NE(it->second, next->second); - } - - // The first entry must not contain the initial value - if (const auto it = map.underlying_.begin(); - it != map.underlying_.end()) { - EXPECT_NE(it->second, map.init_); - } - } -}; - -TEST_F(IntervalMapTest, minimal_interface) { +TEST(interval_map, minimal_interface) { using Key = KeyInterface; using Value = ValueInterface; @@ -145,121 +66,3 @@ TEST_F(IntervalMapTest, minimal_interface) { ASSERT_EQ(map[Key(0)], Value(0)); map.assign(Key(0), Key(1), Value(1)); } - -TEST_F(IntervalMapTest, no_insertion) {} - -TEST_F(IntervalMapTest, insert_begin_equal_end) { - assign(0, 0, 1); -} - -TEST_F(IntervalMapTest, insert_begin_bigger_than_end) { - assign(1, 0, 1); -} - -TEST_F(IntervalMapTest, insert_one_range) { - assign(std::numeric_limits::min(), 0, 1); -} - -TEST_F(IntervalMapTest, insert_non_overlapping_ranges) { - assign(std::numeric_limits::min(), 0, 1); - assign(10, std::numeric_limits::max(), 2); -} - -TEST_F(IntervalMapTest, insert_up_to_max) { - assign(std::numeric_limits::min(), - std::numeric_limits::max(), 1); -} - -TEST_F(IntervalMapTest, insert_range_right_after) { - assign(0, 10, 1); - assign(10, 20, 1); -} - -TEST_F(IntervalMapTest, insert_range_right_before) { - assign(10, 20, 1); - assign(0, 10, 1); -} - -TEST_F(IntervalMapTest, insert_range_middle) { - assign(0, 10, 1); - assign(20, 30, 1); - assign(10, 20, 1); -} - -TEST_F(IntervalMapTest, insert_range_inside_another) { - assign(0, 20, 1); - assign(5, 15, 2); -} - -TEST_F(IntervalMapTest, insert_range_around_another) { - assign(5, 15, 2); - assign(0, 20, 1); -} - -TEST_F(IntervalMapTest, insert_range_overlaps_many) { - assign(0, 10, 1); - assign(10, 20, 2); - assign(20, 30, 3); - assign(30, 40, 4); - assign(40, 50, 5); - assign(0, 50, -1); -} - -TEST_F(IntervalMapTest, insert_range_overlaps_many_init_value) { - assign(0, 10, 1); - assign(10, 20, 2); - assign(20, 30, 3); - assign(30, 40, 4); - assign(40, 50, 5); - assign(0, 50, 0); -} - -TEST_F(IntervalMapTest, insert_range_overlaps_many_oversize) { - assign(0, 10, 1); - assign(10, 20, 2); - assign(20, 30, 3); - assign(30, 40, 4); - assign(40, 50, 5); - assign(-10, 60, -1); -} - -TEST_F(IntervalMapTest, fuzzing_001) { - assign(-50, 20, 1); - assign(40, 80, 2); - assign(-100, -10, 3); -} - -TEST_F(IntervalMapTest, fuzzing_002) { - assign(-100, 90, 1); - assign(0, 120, 2); - assign(-60, 60, 3); -} - -TEST_F(IntervalMapTest, fuzzing_003) { - assign(-80, 70, 1); - assign(-50, 40, 2); - assign(-40, 20, 3); - assign(-110, -10, 4); -} - -TEST_F(IntervalMapTest, randomized_test) { - auto const seed = []() { - std::random_device r; - return r(); - }(); - SCOPED_TRACE(seed); - auto random = std::mt19937_64(seed); - - auto keys = std::uniform_int_distribution( - std::numeric_limits::min(), - std::numeric_limits::max()); - auto values = std::uniform_int_distribution(0, 10); - - for (auto i = 0; i < 1000; ++i) { - auto const start = keys(random); - auto const end = keys(random); - auto const value = values(random); - assign(start, end, value); - check(); - } -}