#include #include #include #include #include #include #include #include #include using namespace NFlatHash; namespace { template struct TJustType { using type = T; }; template struct TTypePack {}; template constexpr void ForEachType(F&& f, TTypePack) { ApplyToMany(std::forward(f), TJustType{}...); } /* Usage example: * * TForEachType::Apply([](auto t) { * using T = GET_TYPE(t); * }); * So T would be: * int on #0 iteration * float on #1 iteration * TString on #2 iteration */ #define GET_TYPE(ti) typename decltype(ti)::type constexpr size_t INIT_SIZE = 32; constexpr size_t BIG_INIT_SIZE = 128; template struct TSimpleKeyGetter { static constexpr T& Apply(T& t) { return t; } static constexpr const T& Apply(const T& t) { return t; } }; template , class ValueEqual = std::equal_to, class KeyGetter = TSimpleKeyGetter, class F, class... Containers> void ForEachTable(F f, TTypePack cs) { ForEachType([&](auto p) { using TProbing = GET_TYPE(p); ForEachType([&](auto sf) { using TSizeFitter = GET_TYPE(sf); ForEachType([&](auto t) { using TContainer = GET_TYPE(t); static_assert(std::is_same_v); using TTable = TTable, KeyEqual, TContainer, KeyGetter, TProbing, TSizeFitter, TSimpleExpander>; f(TJustType{}); }, cs); }, TTypePack{}); }, TTypePack{}); } using TAtomContainers = TTypePack, TDenseContainer>>; using TContainers = TTypePack, TDenseContainer>>; using TRemovalContainers = TTypePack, TRemovalDenseContainer, NSet::TStaticValueMarker<-1>>>; } Y_UNIT_TEST_SUITE(TCommonTableAtomsTest) { Y_UNIT_TEST(InitTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; UNIT_ASSERT(table.empty()); UNIT_ASSERT_EQUAL(table.size(), 0); UNIT_ASSERT_EQUAL(table.bucket_count(), INIT_SIZE); UNIT_ASSERT_EQUAL(table.bucket_size(RandomNumber(INIT_SIZE)), 0); }, TAtomContainers{}); } Y_UNIT_TEST(IteratorTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; auto first = table.begin(); auto last = table.end(); UNIT_ASSERT_EQUAL(first, last); UNIT_ASSERT_EQUAL(std::distance(first, last), 0); auto cFirst = table.cbegin(); auto cLast = table.cend(); UNIT_ASSERT_EQUAL(cFirst, cLast); UNIT_ASSERT_EQUAL(std::distance(cFirst, cLast), 0); }, TAtomContainers{}); } Y_UNIT_TEST(ContainsAndCountTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; for (int i = 0; i < 100; ++i) { UNIT_ASSERT_EQUAL(table.count(i), 0); UNIT_ASSERT(!table.contains(i)); } }, TAtomContainers{}); } Y_UNIT_TEST(FindTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; for (int i = 0; i < 100; ++i) { auto it = table.find(i); UNIT_ASSERT_EQUAL(it, table.end()); } }, TAtomContainers{}); } } Y_UNIT_TEST_SUITE(TCommonTableTest) { Y_UNIT_TEST(InsertTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; UNIT_ASSERT(table.empty()); UNIT_ASSERT_EQUAL(table.size(), 0); int toInsert = RandomNumber(100); UNIT_ASSERT_EQUAL(table.count(toInsert), 0); UNIT_ASSERT(!table.contains(toInsert)); auto p = table.insert(toInsert); UNIT_ASSERT_EQUAL(p.first, table.begin()); UNIT_ASSERT(p.second); UNIT_ASSERT(!table.empty()); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(toInsert), 1); UNIT_ASSERT(table.contains(toInsert)); auto it = table.find(toInsert); UNIT_ASSERT_UNEQUAL(it, table.end()); UNIT_ASSERT_EQUAL(it, table.begin()); UNIT_ASSERT_EQUAL(*it, toInsert); auto p2 = table.insert(toInsert); UNIT_ASSERT_EQUAL(p.first, p2.first); UNIT_ASSERT(!p2.second); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(toInsert), 1); UNIT_ASSERT(table.contains(toInsert)); }, TContainers{}); } Y_UNIT_TEST(ClearTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; TVector toInsert(INIT_SIZE); Iota(toInsert.begin(), toInsert.end(), 0); ShuffleRange(toInsert); toInsert.resize(INIT_SIZE / 3); for (auto i : toInsert) { auto p = table.insert(i); UNIT_ASSERT_EQUAL(*p.first, i); UNIT_ASSERT(p.second); } UNIT_ASSERT_EQUAL(table.size(), toInsert.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table.begin(), table.end()), toInsert.size()); for (auto i : toInsert) { UNIT_ASSERT(table.contains(i)); UNIT_ASSERT_EQUAL(table.count(i), 1); } auto bc = table.bucket_count(); table.clear(); UNIT_ASSERT(table.empty()); UNIT_ASSERT_EQUAL(table.bucket_count(), bc); for (auto i : toInsert) { UNIT_ASSERT(!table.contains(i)); UNIT_ASSERT_EQUAL(table.count(i), 0); } table.insert(toInsert.front()); UNIT_ASSERT(!table.empty()); }, TContainers{}); } Y_UNIT_TEST(CopyMoveTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; TVector toInsert(INIT_SIZE); Iota(toInsert.begin(), toInsert.end(), 0); ShuffleRange(toInsert); toInsert.resize(INIT_SIZE / 3); for (auto i : toInsert) { auto p = table.insert(i); UNIT_ASSERT_EQUAL(*p.first, i); UNIT_ASSERT(p.second); } UNIT_ASSERT_EQUAL(table.size(), toInsert.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table.begin(), table.end()), toInsert.size()); for (auto i : toInsert) { UNIT_ASSERT(table.contains(i)); UNIT_ASSERT_EQUAL(table.count(i), 1); } // Copy construction test auto table2 = table; UNIT_ASSERT_EQUAL(table2.size(), table.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table2.begin(), table2.end()), table.size()); for (auto i : table) { UNIT_ASSERT(table2.contains(i)); UNIT_ASSERT_EQUAL(table2.count(i), 1); } table2.clear(); UNIT_ASSERT(table2.empty()); // Copy assignment test table2 = table; UNIT_ASSERT_EQUAL(table2.size(), table.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table2.begin(), table2.end()), table.size()); for (auto i : table) { UNIT_ASSERT(table2.contains(i)); UNIT_ASSERT_EQUAL(table2.count(i), 1); } // Move construction test auto table3 = std::move(table2); UNIT_ASSERT(table2.empty()); UNIT_ASSERT(table2.bucket_count() > 0); UNIT_ASSERT_EQUAL(table3.size(), table.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table3.begin(), table3.end()), table.size()); for (auto i : table) { UNIT_ASSERT(table3.contains(i)); UNIT_ASSERT_EQUAL(table3.count(i), 1); } table2.insert(toInsert.front()); UNIT_ASSERT(!table2.empty()); UNIT_ASSERT_EQUAL(table2.size(), 1); UNIT_ASSERT_UNEQUAL(table2.bucket_count(), 0); // Move assignment test table2 = std::move(table3); UNIT_ASSERT(table3.empty()); UNIT_ASSERT(table3.bucket_count() > 0); UNIT_ASSERT_EQUAL(table2.size(), table.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table2.begin(), table2.end()), table.size()); for (auto i : table) { UNIT_ASSERT(table2.contains(i)); UNIT_ASSERT_EQUAL(table2.count(i), 1); } table3.insert(toInsert.front()); UNIT_ASSERT(!table3.empty()); UNIT_ASSERT_EQUAL(table3.size(), 1); UNIT_ASSERT_UNEQUAL(table3.bucket_count(), 0); }, TContainers{}); } Y_UNIT_TEST(RehashTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; TVector toInsert(INIT_SIZE); Iota(toInsert.begin(), toInsert.end(), 0); ShuffleRange(toInsert); toInsert.resize(INIT_SIZE / 3); for (auto i : toInsert) { table.insert(i); } auto bc = table.bucket_count(); table.rehash(bc * 2); UNIT_ASSERT(bc * 2 <= table.bucket_count()); UNIT_ASSERT_EQUAL(table.size(), toInsert.size()); UNIT_ASSERT_EQUAL((size_t)std::distance(table.begin(), table.end()), toInsert.size()); for (auto i : toInsert) { UNIT_ASSERT(table.contains(i)); UNIT_ASSERT_EQUAL(table.count(i), 1); } TVector tmp(table.begin(), table.end()); Sort(toInsert.begin(), toInsert.end()); Sort(tmp.begin(), tmp.end()); UNIT_ASSERT_VALUES_EQUAL(tmp, toInsert); table.rehash(0); UNIT_ASSERT_EQUAL(table.size(), toInsert.size()); UNIT_ASSERT(table.bucket_count() > table.size()); table.clear(); UNIT_ASSERT(table.empty()); table.rehash(INIT_SIZE); UNIT_ASSERT(table.bucket_count() >= INIT_SIZE); table.rehash(0); UNIT_ASSERT(table.bucket_count() > 0); }, TContainers{}); } Y_UNIT_TEST(EraseTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ INIT_SIZE }; int value = RandomNumber(); table.insert(value); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(value), 1); auto it = table.find(value); table.erase(it); UNIT_ASSERT_EQUAL(table.size(), 0); UNIT_ASSERT_EQUAL(table.count(value), 0); table.insert(value); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(value), 1); table.erase(value); UNIT_ASSERT_EQUAL(table.size(), 0); UNIT_ASSERT_EQUAL(table.count(value), 0); table.insert(value); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(value), 1); table.erase(table.find(value), table.end()); UNIT_ASSERT_EQUAL(table.size(), 0); UNIT_ASSERT_EQUAL(table.count(value), 0); table.erase(value); UNIT_ASSERT_EQUAL(table.size(), 0); UNIT_ASSERT_EQUAL(table.count(value), 0); }, TRemovalContainers{}); } Y_UNIT_TEST(EraseBigTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ BIG_INIT_SIZE }; for (int i = 0; i < 1000; ++i) { for (int j = 0; j < static_cast(BIG_INIT_SIZE); ++j) { table.emplace(j); } for (int j = 0; j < static_cast(BIG_INIT_SIZE); ++j) { table.erase(j); } } UNIT_ASSERT(table.bucket_count() <= BIG_INIT_SIZE * 8); }, TRemovalContainers{}); } Y_UNIT_TEST(ConstructWithSizeTest) { ForEachTable([](auto t) { GET_TYPE(t) table{ 1000 }; UNIT_ASSERT(table.bucket_count() >= 1000); int value = RandomNumber(); table.insert(value); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(value), 1); UNIT_ASSERT(table.bucket_count() >= 1000); table.rehash(10); UNIT_ASSERT_EQUAL(table.size(), 1); UNIT_ASSERT_EQUAL(table.count(value), 1); UNIT_ASSERT(table.bucket_count() < 1000); UNIT_ASSERT(table.bucket_count() >= 10); }, TContainers{}); } }