/******************************************************************************* * This file is part of the "https://github.com/blackmatov/flat.hpp" * For conditions of distribution and use, see copyright notice in LICENSE.md * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) ******************************************************************************/ #define CATCH_CONFIG_FAST_COMPILE #include #include #include using namespace flat_hpp; namespace { template < typename T > class dummy_less { public: dummy_less() = default; dummy_less(int i) noexcept : i(i) {} bool operator()(const T& l, const T& r) const { return l < r; } int i = 0; }; template < typename T > class dummy_less2 { dummy_less2() = default; dummy_less2(dummy_less2&&) noexcept(false) {} bool operator()(const T& l, const T& r) const { return l < r; } }; template < typename T > void swap(dummy_less2&, dummy_less2&) noexcept { } template < typename T > class dummy_less3 { dummy_less3() = default; dummy_less3(dummy_less3&&) noexcept(false) {} bool operator()(const T& l, const T& r) const { return l < r; } }; template < typename T > constexpr std::add_const_t& my_as_const(T& t) noexcept { return t; } } TEST_CASE("flat_set") { SECTION("sizeof") { REQUIRE(sizeof(flat_set) == sizeof(std::vector)); struct vc : flat_set::value_compare { int i; }; REQUIRE(sizeof(vc) == sizeof(int)); } SECTION("noexcept") { using alloc_t = std::allocator; using set_t = flat_set, std::vector>; using set2_t = flat_set>; using set3_t = flat_set>; STATIC_REQUIRE(std::is_nothrow_default_constructible_v); STATIC_REQUIRE(std::is_nothrow_move_constructible_v); STATIC_REQUIRE(std::is_nothrow_move_assignable_v); STATIC_REQUIRE(std::is_nothrow_swappable_v); STATIC_REQUIRE(std::is_nothrow_swappable_v); STATIC_REQUIRE(!std::is_nothrow_swappable_v); STATIC_REQUIRE(noexcept(std::declval().begin())); STATIC_REQUIRE(noexcept(std::declval().begin())); STATIC_REQUIRE(noexcept(std::declval().cbegin())); STATIC_REQUIRE(noexcept(std::declval().end())); STATIC_REQUIRE(noexcept(std::declval().end())); STATIC_REQUIRE(noexcept(std::declval().cend())); STATIC_REQUIRE(noexcept(std::declval().rbegin())); STATIC_REQUIRE(noexcept(std::declval().rbegin())); STATIC_REQUIRE(noexcept(std::declval().crbegin())); STATIC_REQUIRE(noexcept(std::declval().rend())); STATIC_REQUIRE(noexcept(std::declval().rend())); STATIC_REQUIRE(noexcept(std::declval().crend())); STATIC_REQUIRE(noexcept(std::declval().empty())); STATIC_REQUIRE(noexcept(std::declval().size())); STATIC_REQUIRE(noexcept(std::declval().max_size())); STATIC_REQUIRE(noexcept(std::declval().capacity())); STATIC_REQUIRE(noexcept(std::declval().clear())); } SECTION("types") { using set_t = flat_set; STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); STATIC_REQUIRE(std::is_same_v); } SECTION("ctors") { using alloc_t = std::allocator; using set_t = flat_set, std::vector>; using set2_t = flat_set, std::vector>; using vec_t = std::vector; { auto s0 = set_t(); auto s1 = set2_t(alloc_t()); auto s2 = set_t(std::less()); auto s3 = set2_t(std::greater(), alloc_t()); } { vec_t v{1,2,3}; auto s0 = set_t(v.cbegin(), v.cend()); auto s1 = set2_t(v.cbegin(), v.cend(), alloc_t()); auto s2 = set_t(v.cbegin(), v.cend(), std::less()); auto s3 = set2_t(v.cbegin(), v.cend(), std::greater(), alloc_t()); REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({1,2,3})); REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({3,2,1})); REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({1,2,3})); REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({3,2,1})); } { auto s0 = set_t({0,1,2}); auto s1 = set2_t({0,1,2}, alloc_t()); auto s2 = set_t({0,1,2}, std::less()); auto s3 = set2_t({0,1,2}, std::greater(), alloc_t()); REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({0,1,2})); REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({2,1,0})); REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({0,1,2})); REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({2,1,0})); } { auto s0 = set_t{0,1,2}; auto s1 = s0; REQUIRE(s0 == set_t{0,1,2}); REQUIRE(s1 == set_t{0,1,2}); auto s2 = std::move(s1); REQUIRE(s1.empty()); REQUIRE(s2 == set_t{0,1,2}); auto s3 = set_t(s2, alloc_t()); REQUIRE(s2 == s3); auto s4 = set_t(std::move(s3), alloc_t()); REQUIRE(s3.empty()); REQUIRE(s4 == set_t{0,1,2}); } { auto s0 = set_t{0,1,2}; set_t s1; s1 = s0; REQUIRE(s0 == set_t{0,1,2}); REQUIRE(s1 == set_t{0,1,2}); set_t s2; s2 = std::move(s1); REQUIRE(s0 == set_t{0,1,2}); REQUIRE(s1.empty()); REQUIRE(s2 == set_t{0,1,2}); set_t s3; s3 = {1,2,3}; REQUIRE(s3 == set_t{1,2,3}); } } SECTION("capacity") { using set_t = flat_set; { set_t s0; REQUIRE(s0.empty()); REQUIRE_FALSE(s0.size()); REQUIRE(s0.max_size() == std::allocator().max_size()); s0.insert(42); REQUIRE_FALSE(s0.empty()); REQUIRE(s0.size() == 1u); REQUIRE(s0.max_size() == std::allocator().max_size()); s0.insert(42); REQUIRE(s0.size() == 1u); s0.insert(84); REQUIRE(s0.size() == 2u); s0.clear(); REQUIRE(s0.empty()); REQUIRE_FALSE(s0.size()); REQUIRE(s0.max_size() == std::allocator().max_size()); } { set_t s0; REQUIRE(s0.capacity() == 0); s0.reserve(42); REQUIRE(s0.capacity() == 42); s0.insert({1,2,3}); REQUIRE(s0.capacity() == 42); s0.shrink_to_fit(); REQUIRE(s0.size() == 3); REQUIRE(s0.capacity() == 3); REQUIRE(s0 == set_t{1,2,3}); using alloc2_t = std::allocator; using set2_t = flat_set< int, std::less, std::deque>; set2_t s1; s1.insert({1,2,3}); REQUIRE(s1 == set2_t{1,2,3}); } } SECTION("inserts") { struct obj_t { obj_t(int i) : i(i) {} int i; bool operator<(const obj_t& o) const { return i < o.i; } bool operator==(const obj_t& o) const { return i == o.i; } }; using set_t = flat_set; { set_t s0; auto i0 = s0.insert(1); REQUIRE(s0 == set_t{1}); REQUIRE(i0 == std::make_pair(s0.begin(), true)); auto i1 = s0.insert(obj_t(1)); REQUIRE(s0 == set_t{1}); REQUIRE(i1 == std::make_pair(s0.begin(), false)); auto i2 = s0.insert(obj_t(2)); REQUIRE(s0 == set_t{1,2}); REQUIRE(i2 == std::make_pair(s0.begin() + 1, true)); auto o2 = obj_t(2); auto i3 = s0.insert(o2); REQUIRE(i3 == std::make_pair(s0.begin() + 1, false)); s0.insert(s0.cbegin(), 1); s0.insert(s0.cbegin(), 2); s0.insert(s0.cend(), 1); s0.insert(s0.cend(), 2); REQUIRE(s0 == set_t{1,2}); s0.insert(s0.cbegin(), 0); REQUIRE(s0 == set_t{0,1,2}); s0.insert(s0.cend(), 3); REQUIRE(s0 == set_t{0,1,2,3}); s0.insert(s0.cbegin(), 4); s0.insert(s0.cend(), -1); REQUIRE(s0 == set_t{-1,0,1,2,3,4}); s0.insert(s0.cbegin() + 2, obj_t(5)); REQUIRE(s0 == set_t{-1,0,1,2,3,4,5}); s0.insert(s0.cbegin(), obj_t(-2)); REQUIRE(s0 == set_t{-2,-1,0,1,2,3,4,5}); } { set_t s0; auto e0 = s0.emplace(3); REQUIRE(s0 == set_t{3}); REQUIRE(e0 == std::make_pair(s0.begin(), true)); auto e1 = s0.emplace(obj_t(3)); REQUIRE(e1 == std::make_pair(s0.begin(), false)); auto e2 = s0.emplace(4); REQUIRE(s0 == set_t{3,4}); REQUIRE(e2 == std::make_pair(s0.begin() + 1, true)); auto e3 = s0.emplace_hint(s0.cbegin(), 1); REQUIRE(e3 == s0.begin()); auto e4 = s0.emplace_hint(s0.cend(), 2); REQUIRE(e4 == s0.begin() + 1); s0.emplace_hint(s0.cbegin(), 5); s0.emplace_hint(s0.cend(), 6); REQUIRE(s0 == set_t{1,2,3,4,5,6}); } } SECTION("erasers") { using set_t = flat_set; { set_t s0{1,2,3,4,5}; s0.clear(); REQUIRE(s0.empty()); } { set_t s0{1,2,3,4,5}; auto i = s0.erase(s0.find(3)); REQUIRE(i == s0.begin() + 2); REQUIRE(s0 == set_t{1,2,4,5}); } { set_t s0{1,2,3,4,5}; auto i = s0.erase(s0.begin() + 2, s0.end()); REQUIRE(i == s0.end()); REQUIRE(s0 == set_t{1,2}); } { set_t s0{1,2,3,4,5}; REQUIRE(s0.erase(2) == 1); REQUIRE(s0.erase(6) == 0); REQUIRE(s0 == set_t{1,3,4,5}); } { set_t s0{1,2,3}; set_t s1{3,4,5}; s0.swap(s1); REQUIRE(s0 == set_t{3,4,5}); REQUIRE(s1 == set_t{1,2,3}); swap(s1, s0); REQUIRE(s0 == set_t{1,2,3}); REQUIRE(s1 == set_t{3,4,5}); } } SECTION("lookup") { using set_t = flat_set; { set_t s0{1,2,3,4,5}; REQUIRE(s0.count(3)); REQUIRE_FALSE(s0.count(6)); REQUIRE(my_as_const(s0).count(5)); REQUIRE_FALSE(my_as_const(s0).count(0)); } { set_t s0{1,2,3,4,5}; REQUIRE(s0.find(2) == s0.begin() + 1); REQUIRE(my_as_const(s0).find(3) == s0.cbegin() + 2); REQUIRE(s0.find(6) == s0.end()); REQUIRE(my_as_const(s0).find(0) == s0.cend()); } { set_t s0{1,2,3,4,5}; REQUIRE(s0.equal_range(3) == std::make_pair(s0.begin() + 2, s0.begin() + 3)); REQUIRE(s0.equal_range(6) == std::make_pair(s0.end(), s0.end())); REQUIRE(my_as_const(s0).equal_range(3) == std::make_pair(s0.cbegin() + 2, s0.cbegin() + 3)); REQUIRE(my_as_const(s0).equal_range(0) == std::make_pair(s0.cbegin(), s0.cbegin())); } { set_t s0{0,3,6,9}; REQUIRE(s0.lower_bound(0) == s0.begin()); REQUIRE(s0.lower_bound(1) == s0.begin() + 1); REQUIRE(s0.lower_bound(10) == s0.end()); REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 3); } } SECTION("observers") { struct my_less { int i; my_less(int i) : i(i) {} bool operator()(int l, int r) const { return l < r; } }; using set_t = flat_set; set_t s0(my_less(42)); REQUIRE(my_as_const(s0).key_comp().i == 42); REQUIRE(my_as_const(s0).value_comp().i == 42); } SECTION("custom_less") { using set_t = flat_set>; auto s0 = set_t(dummy_less(42)); auto s1 = set_t(dummy_less(21)); REQUIRE(s0.key_comp().i == 42); REQUIRE(s1.key_comp().i == 21); s0.swap(s1); REQUIRE(s0.key_comp().i == 21); REQUIRE(s1.key_comp().i == 42); } SECTION("operators") { using set_t = flat_set; REQUIRE(set_t{1,2,3} == set_t{3,2,1}); REQUIRE_FALSE(set_t{1,2,3} == set_t{3,2,4}); REQUIRE_FALSE(set_t{1,2,3} == set_t{1,2,3,4}); REQUIRE(set_t{1,2,3} != set_t{3,2,4}); REQUIRE_FALSE(set_t{1,2,3} != set_t{3,2,1}); REQUIRE(set_t{2,3,4,6} < set_t{2,3,5}); REQUIRE(set_t{2,3,4,6} <= set_t{2,3,5}); REQUIRE_FALSE(set_t{2,3,5} < set_t{2,3,4,6}); REQUIRE_FALSE(set_t{2,3,5} <= set_t{2,3,4,6}); REQUIRE_FALSE(set_t{2,3,4,6} > set_t{2,3,5}); REQUIRE_FALSE(set_t{2,3,4,6} >= set_t{2,3,5}); REQUIRE(set_t{2,3,5} > set_t{2,3,4,6}); REQUIRE(set_t{2,3,5} >= set_t{2,3,4,6}); REQUIRE_FALSE(set_t{1,2,3} < set_t{1,2,3}); REQUIRE(set_t{1,2,3} <= set_t{1,2,3}); REQUIRE_FALSE(set_t{1,2,3} > set_t{1,2,3}); REQUIRE(set_t{1,2,3} >= set_t{1,2,3}); const set_t s0; REQUIRE(s0 == s0); REQUIRE_FALSE(s0 != s0); REQUIRE_FALSE(s0 < s0); REQUIRE_FALSE(s0 > s0); REQUIRE(s0 <= s0); REQUIRE(s0 >= s0); } }