| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900 | 
							- // Copyright 2018 The Abseil Authors.
 
- //
 
- // Licensed under the Apache License, Version 2.0 (the "License");
 
- // you may not use this file except in compliance with the License.
 
- // You may obtain a copy of the License at
 
- //
 
- //      https://www.apache.org/licenses/LICENSE-2.0
 
- //
 
- // Unless required by applicable law or agreed to in writing, software
 
- // distributed under the License is distributed on an "AS IS" BASIS,
 
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
- // See the License for the specific language governing permissions and
 
- // limitations under the License.
 
- #include "absl/container/internal/raw_hash_set.h"
 
- #include <cmath>
 
- #include <cstdint>
 
- #include <deque>
 
- #include <functional>
 
- #include <memory>
 
- #include <numeric>
 
- #include <random>
 
- #include <string>
 
- #include "gmock/gmock.h"
 
- #include "gtest/gtest.h"
 
- #include "absl/base/attributes.h"
 
- #include "absl/base/internal/cycleclock.h"
 
- #include "absl/base/internal/raw_logging.h"
 
- #include "absl/container/internal/container_memory.h"
 
- #include "absl/container/internal/hash_function_defaults.h"
 
- #include "absl/container/internal/hash_policy_testing.h"
 
- #include "absl/container/internal/hashtable_debug.h"
 
- #include "absl/strings/string_view.h"
 
- namespace absl {
 
- namespace container_internal {
 
- struct RawHashSetTestOnlyAccess {
 
-   template <typename C>
 
-   static auto GetSlots(const C& c) -> decltype(c.slots_) {
 
-     return c.slots_;
 
-   }
 
- };
 
- namespace {
 
- using ::testing::DoubleNear;
 
- using ::testing::ElementsAre;
 
- using ::testing::Ge;
 
- using ::testing::Lt;
 
- using ::testing::Optional;
 
- using ::testing::Pair;
 
- using ::testing::UnorderedElementsAre;
 
- TEST(Util, NormalizeCapacity) {
 
-   EXPECT_EQ(1, NormalizeCapacity(0));
 
-   EXPECT_EQ(1, NormalizeCapacity(1));
 
-   EXPECT_EQ(3, NormalizeCapacity(2));
 
-   EXPECT_EQ(3, NormalizeCapacity(3));
 
-   EXPECT_EQ(7, NormalizeCapacity(4));
 
-   EXPECT_EQ(7, NormalizeCapacity(7));
 
-   EXPECT_EQ(15, NormalizeCapacity(8));
 
-   EXPECT_EQ(15, NormalizeCapacity(15));
 
-   EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 1));
 
-   EXPECT_EQ(15 * 2 + 1, NormalizeCapacity(15 + 2));
 
- }
 
- TEST(Util, GrowthAndCapacity) {
 
-   // Verify that GrowthToCapacity gives the minimum capacity that has enough
 
-   // growth.
 
-   for (size_t growth = 0; growth < 10000; ++growth) {
 
-     SCOPED_TRACE(growth);
 
-     size_t capacity = NormalizeCapacity(GrowthToLowerboundCapacity(growth));
 
-     // The capacity is large enough for `growth`
 
-     EXPECT_THAT(CapacityToGrowth(capacity), Ge(growth));
 
-     if (growth != 0 && capacity > 1) {
 
-       // There is no smaller capacity that works.
 
-       EXPECT_THAT(CapacityToGrowth(capacity / 2), Lt(growth));
 
-     }
 
-   }
 
-   for (size_t capacity = Group::kWidth - 1; capacity < 10000;
 
-        capacity = 2 * capacity + 1) {
 
-     SCOPED_TRACE(capacity);
 
-     size_t growth = CapacityToGrowth(capacity);
 
-     EXPECT_THAT(growth, Lt(capacity));
 
-     EXPECT_LE(GrowthToLowerboundCapacity(growth), capacity);
 
-     EXPECT_EQ(NormalizeCapacity(GrowthToLowerboundCapacity(growth)), capacity);
 
-   }
 
- }
 
- TEST(Util, probe_seq) {
 
-   probe_seq<16> seq(0, 127);
 
-   auto gen = [&]() {
 
-     size_t res = seq.offset();
 
-     seq.next();
 
-     return res;
 
-   };
 
-   std::vector<size_t> offsets(8);
 
-   std::generate_n(offsets.begin(), 8, gen);
 
-   EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
 
-   seq = probe_seq<16>(128, 127);
 
-   std::generate_n(offsets.begin(), 8, gen);
 
-   EXPECT_THAT(offsets, ElementsAre(0, 16, 48, 96, 32, 112, 80, 64));
 
- }
 
- TEST(BitMask, Smoke) {
 
-   EXPECT_FALSE((BitMask<uint8_t, 8>(0)));
 
-   EXPECT_TRUE((BitMask<uint8_t, 8>(5)));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0)), ElementsAre());
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0x1)), ElementsAre(0));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0x2)), ElementsAre(1));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0x3)), ElementsAre(0, 1));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0x4)), ElementsAre(2));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0x5)), ElementsAre(0, 2));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0x55)), ElementsAre(0, 2, 4, 6));
 
-   EXPECT_THAT((BitMask<uint8_t, 8>(0xAA)), ElementsAre(1, 3, 5, 7));
 
- }
 
- TEST(BitMask, WithShift) {
 
-   // See the non-SSE version of Group for details on what this math is for.
 
-   uint64_t ctrl = 0x1716151413121110;
 
-   uint64_t hash = 0x12;
 
-   constexpr uint64_t msbs = 0x8080808080808080ULL;
 
-   constexpr uint64_t lsbs = 0x0101010101010101ULL;
 
-   auto x = ctrl ^ (lsbs * hash);
 
-   uint64_t mask = (x - lsbs) & ~x & msbs;
 
-   EXPECT_EQ(0x0000000080800000, mask);
 
-   BitMask<uint64_t, 8, 3> b(mask);
 
-   EXPECT_EQ(*b, 2);
 
- }
 
- TEST(BitMask, LeadingTrailing) {
 
-   EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).LeadingZeros()), 3);
 
-   EXPECT_EQ((BitMask<uint32_t, 16>(0x00001a40).TrailingZeros()), 6);
 
-   EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).LeadingZeros()), 15);
 
-   EXPECT_EQ((BitMask<uint32_t, 16>(0x00000001).TrailingZeros()), 0);
 
-   EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).LeadingZeros()), 0);
 
-   EXPECT_EQ((BitMask<uint32_t, 16>(0x00008000).TrailingZeros()), 15);
 
-   EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).LeadingZeros()), 3);
 
-   EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000008080808000).TrailingZeros()), 1);
 
-   EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).LeadingZeros()), 7);
 
-   EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x0000000000000080).TrailingZeros()), 0);
 
-   EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).LeadingZeros()), 0);
 
-   EXPECT_EQ((BitMask<uint64_t, 8, 3>(0x8000000000000000).TrailingZeros()), 7);
 
- }
 
- TEST(Group, EmptyGroup) {
 
-   for (h2_t h = 0; h != 128; ++h) EXPECT_FALSE(Group{EmptyGroup()}.Match(h));
 
- }
 
- TEST(Group, Match) {
 
-   if (Group::kWidth == 16) {
 
-     ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
 
-                       7,      5, 3,        1, 1,      1, 1,         1};
 
-     EXPECT_THAT(Group{group}.Match(0), ElementsAre());
 
-     EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 11, 12, 13, 14, 15));
 
-     EXPECT_THAT(Group{group}.Match(3), ElementsAre(3, 10));
 
-     EXPECT_THAT(Group{group}.Match(5), ElementsAre(5, 9));
 
-     EXPECT_THAT(Group{group}.Match(7), ElementsAre(7, 8));
 
-   } else if (Group::kWidth == 8) {
 
-     ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
 
-     EXPECT_THAT(Group{group}.Match(0), ElementsAre());
 
-     EXPECT_THAT(Group{group}.Match(1), ElementsAre(1, 5, 7));
 
-     EXPECT_THAT(Group{group}.Match(2), ElementsAre(2, 4));
 
-   } else {
 
-     FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
 
-   }
 
- }
 
- TEST(Group, MatchEmpty) {
 
-   if (Group::kWidth == 16) {
 
-     ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
 
-                       7,      5, 3,        1, 1,      1, 1,         1};
 
-     EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0, 4));
 
-   } else if (Group::kWidth == 8) {
 
-     ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
 
-     EXPECT_THAT(Group{group}.MatchEmpty(), ElementsAre(0));
 
-   } else {
 
-     FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
 
-   }
 
- }
 
- TEST(Group, MatchEmptyOrDeleted) {
 
-   if (Group::kWidth == 16) {
 
-     ctrl_t group[] = {kEmpty, 1, kDeleted, 3, kEmpty, 5, kSentinel, 7,
 
-                       7,      5, 3,        1, 1,      1, 1,         1};
 
-     EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 2, 4));
 
-   } else if (Group::kWidth == 8) {
 
-     ctrl_t group[] = {kEmpty, 1, 2, kDeleted, 2, 1, kSentinel, 1};
 
-     EXPECT_THAT(Group{group}.MatchEmptyOrDeleted(), ElementsAre(0, 3));
 
-   } else {
 
-     FAIL() << "No test coverage for Group::kWidth==" << Group::kWidth;
 
-   }
 
- }
 
- TEST(Batch, DropDeletes) {
 
-   constexpr size_t kCapacity = 63;
 
-   constexpr size_t kGroupWidth = container_internal::Group::kWidth;
 
-   std::vector<ctrl_t> ctrl(kCapacity + 1 + kGroupWidth);
 
-   ctrl[kCapacity] = kSentinel;
 
-   std::vector<ctrl_t> pattern = {kEmpty, 2, kDeleted, 2, kEmpty, 1, kDeleted};
 
-   for (size_t i = 0; i != kCapacity; ++i) {
 
-     ctrl[i] = pattern[i % pattern.size()];
 
-     if (i < kGroupWidth - 1)
 
-       ctrl[i + kCapacity + 1] = pattern[i % pattern.size()];
 
-   }
 
-   ConvertDeletedToEmptyAndFullToDeleted(ctrl.data(), kCapacity);
 
-   ASSERT_EQ(ctrl[kCapacity], kSentinel);
 
-   for (size_t i = 0; i < kCapacity + 1 + kGroupWidth; ++i) {
 
-     ctrl_t expected = pattern[i % (kCapacity + 1) % pattern.size()];
 
-     if (i == kCapacity) expected = kSentinel;
 
-     if (expected == kDeleted) expected = kEmpty;
 
-     if (IsFull(expected)) expected = kDeleted;
 
-     EXPECT_EQ(ctrl[i], expected)
 
-         << i << " " << int{pattern[i % pattern.size()]};
 
-   }
 
- }
 
- TEST(Group, CountLeadingEmptyOrDeleted) {
 
-   const std::vector<ctrl_t> empty_examples = {kEmpty, kDeleted};
 
-   const std::vector<ctrl_t> full_examples = {0, 1, 2, 3, 5, 9, 127, kSentinel};
 
-   for (ctrl_t empty : empty_examples) {
 
-     std::vector<ctrl_t> e(Group::kWidth, empty);
 
-     EXPECT_EQ(Group::kWidth, Group{e.data()}.CountLeadingEmptyOrDeleted());
 
-     for (ctrl_t full : full_examples) {
 
-       for (size_t i = 0; i != Group::kWidth; ++i) {
 
-         std::vector<ctrl_t> f(Group::kWidth, empty);
 
-         f[i] = full;
 
-         EXPECT_EQ(i, Group{f.data()}.CountLeadingEmptyOrDeleted());
 
-       }
 
-       std::vector<ctrl_t> f(Group::kWidth, empty);
 
-       f[Group::kWidth * 2 / 3] = full;
 
-       f[Group::kWidth / 2] = full;
 
-       EXPECT_EQ(
 
-           Group::kWidth / 2, Group{f.data()}.CountLeadingEmptyOrDeleted());
 
-     }
 
-   }
 
- }
 
- struct IntPolicy {
 
-   using slot_type = int64_t;
 
-   using key_type = int64_t;
 
-   using init_type = int64_t;
 
-   static void construct(void*, int64_t* slot, int64_t v) { *slot = v; }
 
-   static void destroy(void*, int64_t*) {}
 
-   static void transfer(void*, int64_t* new_slot, int64_t* old_slot) {
 
-     *new_slot = *old_slot;
 
-   }
 
-   static int64_t& element(slot_type* slot) { return *slot; }
 
-   template <class F>
 
-   static auto apply(F&& f, int64_t x) -> decltype(std::forward<F>(f)(x, x)) {
 
-     return std::forward<F>(f)(x, x);
 
-   }
 
- };
 
- class StringPolicy {
 
-   template <class F, class K, class V,
 
-             class = typename std::enable_if<
 
-                 std::is_convertible<const K&, absl::string_view>::value>::type>
 
-   decltype(std::declval<F>()(
 
-       std::declval<const absl::string_view&>(), std::piecewise_construct,
 
-       std::declval<std::tuple<K>>(),
 
-       std::declval<V>())) static apply_impl(F&& f,
 
-                                             std::pair<std::tuple<K>, V> p) {
 
-     const absl::string_view& key = std::get<0>(p.first);
 
-     return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first),
 
-                               std::move(p.second));
 
-   }
 
-  public:
 
-   struct slot_type {
 
-     struct ctor {};
 
-     template <class... Ts>
 
-     slot_type(ctor, Ts&&... ts) : pair(std::forward<Ts>(ts)...) {}
 
-     std::pair<std::string, std::string> pair;
 
-   };
 
-   using key_type = std::string;
 
-   using init_type = std::pair<std::string, std::string>;
 
-   template <class allocator_type, class... Args>
 
-   static void construct(allocator_type* alloc, slot_type* slot, Args... args) {
 
-     std::allocator_traits<allocator_type>::construct(
 
-         *alloc, slot, typename slot_type::ctor(), std::forward<Args>(args)...);
 
-   }
 
-   template <class allocator_type>
 
-   static void destroy(allocator_type* alloc, slot_type* slot) {
 
-     std::allocator_traits<allocator_type>::destroy(*alloc, slot);
 
-   }
 
-   template <class allocator_type>
 
-   static void transfer(allocator_type* alloc, slot_type* new_slot,
 
-                        slot_type* old_slot) {
 
-     construct(alloc, new_slot, std::move(old_slot->pair));
 
-     destroy(alloc, old_slot);
 
-   }
 
-   static std::pair<std::string, std::string>& element(slot_type* slot) {
 
-     return slot->pair;
 
-   }
 
-   template <class F, class... Args>
 
-   static auto apply(F&& f, Args&&... args)
 
-       -> decltype(apply_impl(std::forward<F>(f),
 
-                              PairArgs(std::forward<Args>(args)...))) {
 
-     return apply_impl(std::forward<F>(f),
 
-                       PairArgs(std::forward<Args>(args)...));
 
-   }
 
- };
 
- struct StringHash : absl::Hash<absl::string_view> {
 
-   using is_transparent = void;
 
- };
 
- struct StringEq : std::equal_to<absl::string_view> {
 
-   using is_transparent = void;
 
- };
 
- struct StringTable
 
-     : raw_hash_set<StringPolicy, StringHash, StringEq, std::allocator<int>> {
 
-   using Base = typename StringTable::raw_hash_set;
 
-   StringTable() {}
 
-   using Base::Base;
 
- };
 
- struct IntTable
 
-     : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
 
-                    std::equal_to<int64_t>, std::allocator<int64_t>> {
 
-   using Base = typename IntTable::raw_hash_set;
 
-   IntTable() {}
 
-   using Base::Base;
 
- };
 
- struct BadFastHash {
 
-   template <class T>
 
-   size_t operator()(const T&) const {
 
-     return 0;
 
-   }
 
- };
 
- struct BadTable : raw_hash_set<IntPolicy, BadFastHash, std::equal_to<int>,
 
-                                std::allocator<int>> {
 
-   using Base = typename BadTable::raw_hash_set;
 
-   BadTable() {}
 
-   using Base::Base;
 
- };
 
- TEST(Table, EmptyFunctorOptimization) {
 
-   static_assert(std::is_empty<std::equal_to<absl::string_view>>::value, "");
 
-   static_assert(std::is_empty<std::allocator<int>>::value, "");
 
-   struct MockTable {
 
-     void* ctrl;
 
-     void* slots;
 
-     size_t size;
 
-     size_t capacity;
 
-     size_t growth_left;
 
-     void* infoz;
 
-   };
 
-   struct StatelessHash {
 
-     size_t operator()(absl::string_view) const { return 0; }
 
-   };
 
-   struct StatefulHash : StatelessHash {
 
-     size_t dummy;
 
-   };
 
-   EXPECT_EQ(
 
-       sizeof(MockTable),
 
-       sizeof(
 
-           raw_hash_set<StringPolicy, StatelessHash,
 
-                        std::equal_to<absl::string_view>, std::allocator<int>>));
 
-   EXPECT_EQ(
 
-       sizeof(MockTable) + sizeof(StatefulHash),
 
-       sizeof(
 
-           raw_hash_set<StringPolicy, StatefulHash,
 
-                        std::equal_to<absl::string_view>, std::allocator<int>>));
 
- }
 
- TEST(Table, Empty) {
 
-   IntTable t;
 
-   EXPECT_EQ(0, t.size());
 
-   EXPECT_TRUE(t.empty());
 
- }
 
- #ifdef __GNUC__
 
- template <class T>
 
- ABSL_ATTRIBUTE_ALWAYS_INLINE inline void DoNotOptimize(const T& v) {
 
-   asm volatile("" : : "r,m"(v) : "memory");
 
- }
 
- #endif
 
- TEST(Table, Prefetch) {
 
-   IntTable t;
 
-   t.emplace(1);
 
-   // Works for both present and absent keys.
 
-   t.prefetch(1);
 
-   t.prefetch(2);
 
-   // Do not run in debug mode, when prefetch is not implemented, or when
 
-   // sanitizers are enabled.
 
- #if defined(NDEBUG) && defined(__GNUC__) && !defined(ADDRESS_SANITIZER) && \
 
-     !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) &&            \
 
-     !defined(UNDEFINED_BEHAVIOR_SANITIZER)
 
-   const auto now = [] { return absl::base_internal::CycleClock::Now(); };
 
-   // Make size enough to not fit in L2 cache (16.7 Mb)
 
-   static constexpr int size = 1 << 22;
 
-   for (int i = 0; i < size; ++i) t.insert(i);
 
-   int64_t no_prefetch = 0, prefetch = 0;
 
-   for (int iter = 0; iter < 10; ++iter) {
 
-     int64_t time = now();
 
-     for (int i = 0; i < size; ++i) {
 
-       DoNotOptimize(t.find(i));
 
-     }
 
-     no_prefetch += now() - time;
 
-     time = now();
 
-     for (int i = 0; i < size; ++i) {
 
-       t.prefetch(i + 20);
 
-       DoNotOptimize(t.find(i));
 
-     }
 
-     prefetch += now() - time;
 
-   }
 
-   // no_prefetch is at least 30% slower.
 
-   EXPECT_GE(1.0 * no_prefetch / prefetch, 1.3);
 
- #endif
 
- }
 
- TEST(Table, LookupEmpty) {
 
-   IntTable t;
 
-   auto it = t.find(0);
 
-   EXPECT_TRUE(it == t.end());
 
- }
 
- TEST(Table, Insert1) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   auto res = t.emplace(0);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_THAT(*res.first, 0);
 
-   EXPECT_EQ(1, t.size());
 
-   EXPECT_THAT(*t.find(0), 0);
 
- }
 
- TEST(Table, Insert2) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   auto res = t.emplace(0);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_THAT(*res.first, 0);
 
-   EXPECT_EQ(1, t.size());
 
-   EXPECT_TRUE(t.find(1) == t.end());
 
-   res = t.emplace(1);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_THAT(*res.first, 1);
 
-   EXPECT_EQ(2, t.size());
 
-   EXPECT_THAT(*t.find(0), 0);
 
-   EXPECT_THAT(*t.find(1), 1);
 
- }
 
- TEST(Table, InsertCollision) {
 
-   BadTable t;
 
-   EXPECT_TRUE(t.find(1) == t.end());
 
-   auto res = t.emplace(1);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_THAT(*res.first, 1);
 
-   EXPECT_EQ(1, t.size());
 
-   EXPECT_TRUE(t.find(2) == t.end());
 
-   res = t.emplace(2);
 
-   EXPECT_THAT(*res.first, 2);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_EQ(2, t.size());
 
-   EXPECT_THAT(*t.find(1), 1);
 
-   EXPECT_THAT(*t.find(2), 2);
 
- }
 
- // Test that we do not add existent element in case we need to search through
 
- // many groups with deleted elements
 
- TEST(Table, InsertCollisionAndFindAfterDelete) {
 
-   BadTable t;  // all elements go to the same group.
 
-   // Have at least 2 groups with Group::kWidth collisions
 
-   // plus some extra collisions in the last group.
 
-   constexpr size_t kNumInserts = Group::kWidth * 2 + 5;
 
-   for (size_t i = 0; i < kNumInserts; ++i) {
 
-     auto res = t.emplace(i);
 
-     EXPECT_TRUE(res.second);
 
-     EXPECT_THAT(*res.first, i);
 
-     EXPECT_EQ(i + 1, t.size());
 
-   }
 
-   // Remove elements one by one and check
 
-   // that we still can find all other elements.
 
-   for (size_t i = 0; i < kNumInserts; ++i) {
 
-     EXPECT_EQ(1, t.erase(i)) << i;
 
-     for (size_t j = i + 1; j < kNumInserts; ++j) {
 
-       EXPECT_THAT(*t.find(j), j);
 
-       auto res = t.emplace(j);
 
-       EXPECT_FALSE(res.second) << i << " " << j;
 
-       EXPECT_THAT(*res.first, j);
 
-       EXPECT_EQ(kNumInserts - i - 1, t.size());
 
-     }
 
-   }
 
-   EXPECT_TRUE(t.empty());
 
- }
 
- TEST(Table, LazyEmplace) {
 
-   StringTable t;
 
-   bool called = false;
 
-   auto it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
 
-     called = true;
 
-     f("abc", "ABC");
 
-   });
 
-   EXPECT_TRUE(called);
 
-   EXPECT_THAT(*it, Pair("abc", "ABC"));
 
-   called = false;
 
-   it = t.lazy_emplace("abc", [&](const StringTable::constructor& f) {
 
-     called = true;
 
-     f("abc", "DEF");
 
-   });
 
-   EXPECT_FALSE(called);
 
-   EXPECT_THAT(*it, Pair("abc", "ABC"));
 
- }
 
- TEST(Table, ContainsEmpty) {
 
-   IntTable t;
 
-   EXPECT_FALSE(t.contains(0));
 
- }
 
- TEST(Table, Contains1) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.insert(0).second);
 
-   EXPECT_TRUE(t.contains(0));
 
-   EXPECT_FALSE(t.contains(1));
 
-   EXPECT_EQ(1, t.erase(0));
 
-   EXPECT_FALSE(t.contains(0));
 
- }
 
- TEST(Table, Contains2) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.insert(0).second);
 
-   EXPECT_TRUE(t.contains(0));
 
-   EXPECT_FALSE(t.contains(1));
 
-   t.clear();
 
-   EXPECT_FALSE(t.contains(0));
 
- }
 
- int decompose_constructed;
 
- struct DecomposeType {
 
-   DecomposeType(int i) : i(i) {  // NOLINT
 
-     ++decompose_constructed;
 
-   }
 
-   explicit DecomposeType(const char* d) : DecomposeType(*d) {}
 
-   int i;
 
- };
 
- struct DecomposeHash {
 
-   using is_transparent = void;
 
-   size_t operator()(DecomposeType a) const { return a.i; }
 
-   size_t operator()(int a) const { return a; }
 
-   size_t operator()(const char* a) const { return *a; }
 
- };
 
- struct DecomposeEq {
 
-   using is_transparent = void;
 
-   bool operator()(DecomposeType a, DecomposeType b) const { return a.i == b.i; }
 
-   bool operator()(DecomposeType a, int b) const { return a.i == b; }
 
-   bool operator()(DecomposeType a, const char* b) const { return a.i == *b; }
 
- };
 
- struct DecomposePolicy {
 
-   using slot_type = DecomposeType;
 
-   using key_type = DecomposeType;
 
-   using init_type = DecomposeType;
 
-   template <typename T>
 
-   static void construct(void*, DecomposeType* slot, T&& v) {
 
-     *slot = DecomposeType(std::forward<T>(v));
 
-   }
 
-   static void destroy(void*, DecomposeType*) {}
 
-   static DecomposeType& element(slot_type* slot) { return *slot; }
 
-   template <class F, class T>
 
-   static auto apply(F&& f, const T& x) -> decltype(std::forward<F>(f)(x, x)) {
 
-     return std::forward<F>(f)(x, x);
 
-   }
 
- };
 
- template <typename Hash, typename Eq>
 
- void TestDecompose(bool construct_three) {
 
-   DecomposeType elem{0};
 
-   const int one = 1;
 
-   const char* three_p = "3";
 
-   const auto& three = three_p;
 
-   raw_hash_set<DecomposePolicy, Hash, Eq, std::allocator<int>> set1;
 
-   decompose_constructed = 0;
 
-   int expected_constructed = 0;
 
-   EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   set1.insert(elem);
 
-   EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   set1.insert(1);
 
-   EXPECT_EQ(++expected_constructed, decompose_constructed);
 
-   set1.emplace("3");
 
-   EXPECT_EQ(++expected_constructed, decompose_constructed);
 
-   EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   {  // insert(T&&)
 
-     set1.insert(1);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   }
 
-   {  // insert(const T&)
 
-     set1.insert(one);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   }
 
-   {  // insert(hint, T&&)
 
-     set1.insert(set1.begin(), 1);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   }
 
-   {  // insert(hint, const T&)
 
-     set1.insert(set1.begin(), one);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   }
 
-   {  // emplace(...)
 
-     set1.emplace(1);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-     set1.emplace("3");
 
-     expected_constructed += construct_three;
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-     set1.emplace(one);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-     set1.emplace(three);
 
-     expected_constructed += construct_three;
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   }
 
-   {  // emplace_hint(...)
 
-     set1.emplace_hint(set1.begin(), 1);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-     set1.emplace_hint(set1.begin(), "3");
 
-     expected_constructed += construct_three;
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-     set1.emplace_hint(set1.begin(), one);
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-     set1.emplace_hint(set1.begin(), three);
 
-     expected_constructed += construct_three;
 
-     EXPECT_EQ(expected_constructed, decompose_constructed);
 
-   }
 
- }
 
- TEST(Table, Decompose) {
 
-   TestDecompose<DecomposeHash, DecomposeEq>(false);
 
-   struct TransparentHashIntOverload {
 
-     size_t operator()(DecomposeType a) const { return a.i; }
 
-     size_t operator()(int a) const { return a; }
 
-   };
 
-   struct TransparentEqIntOverload {
 
-     bool operator()(DecomposeType a, DecomposeType b) const {
 
-       return a.i == b.i;
 
-     }
 
-     bool operator()(DecomposeType a, int b) const { return a.i == b; }
 
-   };
 
-   TestDecompose<TransparentHashIntOverload, DecomposeEq>(true);
 
-   TestDecompose<TransparentHashIntOverload, TransparentEqIntOverload>(true);
 
-   TestDecompose<DecomposeHash, TransparentEqIntOverload>(true);
 
- }
 
- // Returns the largest m such that a table with m elements has the same number
 
- // of buckets as a table with n elements.
 
- size_t MaxDensitySize(size_t n) {
 
-   IntTable t;
 
-   t.reserve(n);
 
-   for (size_t i = 0; i != n; ++i) t.emplace(i);
 
-   const size_t c = t.bucket_count();
 
-   while (c == t.bucket_count()) t.emplace(n++);
 
-   return t.size() - 1;
 
- }
 
- struct Modulo1000Hash {
 
-   size_t operator()(int x) const { return x % 1000; }
 
- };
 
- struct Modulo1000HashTable
 
-     : public raw_hash_set<IntPolicy, Modulo1000Hash, std::equal_to<int>,
 
-                           std::allocator<int>> {};
 
- // Test that rehash with no resize happen in case of many deleted slots.
 
- TEST(Table, RehashWithNoResize) {
 
-   Modulo1000HashTable t;
 
-   // Adding the same length (and the same hash) strings
 
-   // to have at least kMinFullGroups groups
 
-   // with Group::kWidth collisions. Then fill up to MaxDensitySize;
 
-   const size_t kMinFullGroups = 7;
 
-   std::vector<int> keys;
 
-   for (size_t i = 0; i < MaxDensitySize(Group::kWidth * kMinFullGroups); ++i) {
 
-     int k = i * 1000;
 
-     t.emplace(k);
 
-     keys.push_back(k);
 
-   }
 
-   const size_t capacity = t.capacity();
 
-   // Remove elements from all groups except the first and the last one.
 
-   // All elements removed from full groups will be marked as kDeleted.
 
-   const size_t erase_begin = Group::kWidth / 2;
 
-   const size_t erase_end = (t.size() / Group::kWidth - 1) * Group::kWidth;
 
-   for (size_t i = erase_begin; i < erase_end; ++i) {
 
-     EXPECT_EQ(1, t.erase(keys[i])) << i;
 
-   }
 
-   keys.erase(keys.begin() + erase_begin, keys.begin() + erase_end);
 
-   auto last_key = keys.back();
 
-   size_t last_key_num_probes = GetHashtableDebugNumProbes(t, last_key);
 
-   // Make sure that we have to make a lot of probes for last key.
 
-   ASSERT_GT(last_key_num_probes, kMinFullGroups);
 
-   int x = 1;
 
-   // Insert and erase one element, before inplace rehash happen.
 
-   while (last_key_num_probes == GetHashtableDebugNumProbes(t, last_key)) {
 
-     t.emplace(x);
 
-     ASSERT_EQ(capacity, t.capacity());
 
-     // All elements should be there.
 
-     ASSERT_TRUE(t.find(x) != t.end()) << x;
 
-     for (const auto& k : keys) {
 
-       ASSERT_TRUE(t.find(k) != t.end()) << k;
 
-     }
 
-     t.erase(x);
 
-     ++x;
 
-   }
 
- }
 
- TEST(Table, InsertEraseStressTest) {
 
-   IntTable t;
 
-   const size_t kMinElementCount = 250;
 
-   std::deque<int> keys;
 
-   size_t i = 0;
 
-   for (; i < MaxDensitySize(kMinElementCount); ++i) {
 
-     t.emplace(i);
 
-     keys.push_back(i);
 
-   }
 
-   const size_t kNumIterations = 1000000;
 
-   for (; i < kNumIterations; ++i) {
 
-     ASSERT_EQ(1, t.erase(keys.front()));
 
-     keys.pop_front();
 
-     t.emplace(i);
 
-     keys.push_back(i);
 
-   }
 
- }
 
- TEST(Table, InsertOverloads) {
 
-   StringTable t;
 
-   // These should all trigger the insert(init_type) overload.
 
-   t.insert({{}, {}});
 
-   t.insert({"ABC", {}});
 
-   t.insert({"DEF", "!!!"});
 
-   EXPECT_THAT(t, UnorderedElementsAre(Pair("", ""), Pair("ABC", ""),
 
-                                       Pair("DEF", "!!!")));
 
- }
 
- TEST(Table, LargeTable) {
 
-   IntTable t;
 
-   for (int64_t i = 0; i != 100000; ++i) t.emplace(i << 40);
 
-   for (int64_t i = 0; i != 100000; ++i) ASSERT_EQ(i << 40, *t.find(i << 40));
 
- }
 
- // Timeout if copy is quadratic as it was in Rust.
 
- TEST(Table, EnsureNonQuadraticAsInRust) {
 
-   static const size_t kLargeSize = 1 << 15;
 
-   IntTable t;
 
-   for (size_t i = 0; i != kLargeSize; ++i) {
 
-     t.insert(i);
 
-   }
 
-   // If this is quadratic, the test will timeout.
 
-   IntTable t2;
 
-   for (const auto& entry : t) t2.insert(entry);
 
- }
 
- TEST(Table, ClearBug) {
 
-   IntTable t;
 
-   constexpr size_t capacity = container_internal::Group::kWidth - 1;
 
-   constexpr size_t max_size = capacity / 2 + 1;
 
-   for (size_t i = 0; i < max_size; ++i) {
 
-     t.insert(i);
 
-   }
 
-   ASSERT_EQ(capacity, t.capacity());
 
-   intptr_t original = reinterpret_cast<intptr_t>(&*t.find(2));
 
-   t.clear();
 
-   ASSERT_EQ(capacity, t.capacity());
 
-   for (size_t i = 0; i < max_size; ++i) {
 
-     t.insert(i);
 
-   }
 
-   ASSERT_EQ(capacity, t.capacity());
 
-   intptr_t second = reinterpret_cast<intptr_t>(&*t.find(2));
 
-   // We are checking that original and second are close enough to each other
 
-   // that they are probably still in the same group.  This is not strictly
 
-   // guaranteed.
 
-   EXPECT_LT(std::abs(original - second),
 
-             capacity * sizeof(IntTable::value_type));
 
- }
 
- TEST(Table, Erase) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   auto res = t.emplace(0);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_EQ(1, t.size());
 
-   t.erase(res.first);
 
-   EXPECT_EQ(0, t.size());
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
- }
 
- TEST(Table, EraseMaintainsValidIterator) {
 
-   IntTable t;
 
-   const int kNumElements = 100;
 
-   for (int i = 0; i < kNumElements; i ++) {
 
-     EXPECT_TRUE(t.emplace(i).second);
 
-   }
 
-   EXPECT_EQ(t.size(), kNumElements);
 
-   int num_erase_calls = 0;
 
-   auto it = t.begin();
 
-   while (it != t.end()) {
 
-     t.erase(it++);
 
-     num_erase_calls++;
 
-   }
 
-   EXPECT_TRUE(t.empty());
 
-   EXPECT_EQ(num_erase_calls, kNumElements);
 
- }
 
- // Collect N bad keys by following algorithm:
 
- // 1. Create an empty table and reserve it to 2 * N.
 
- // 2. Insert N random elements.
 
- // 3. Take first Group::kWidth - 1 to bad_keys array.
 
- // 4. Clear the table without resize.
 
- // 5. Go to point 2 while N keys not collected
 
- std::vector<int64_t> CollectBadMergeKeys(size_t N) {
 
-   static constexpr int kGroupSize = Group::kWidth - 1;
 
-   auto topk_range = [](size_t b, size_t e, IntTable* t) -> std::vector<int64_t> {
 
-     for (size_t i = b; i != e; ++i) {
 
-       t->emplace(i);
 
-     }
 
-     std::vector<int64_t> res;
 
-     res.reserve(kGroupSize);
 
-     auto it = t->begin();
 
-     for (size_t i = b; i != e && i != b + kGroupSize; ++i, ++it) {
 
-       res.push_back(*it);
 
-     }
 
-     return res;
 
-   };
 
-   std::vector<int64_t> bad_keys;
 
-   bad_keys.reserve(N);
 
-   IntTable t;
 
-   t.reserve(N * 2);
 
-   for (size_t b = 0; bad_keys.size() < N; b += N) {
 
-     auto keys = topk_range(b, b + N, &t);
 
-     bad_keys.insert(bad_keys.end(), keys.begin(), keys.end());
 
-     t.erase(t.begin(), t.end());
 
-     EXPECT_TRUE(t.empty());
 
-   }
 
-   return bad_keys;
 
- }
 
- struct ProbeStats {
 
-   // Number of elements with specific probe length over all tested tables.
 
-   std::vector<size_t> all_probes_histogram;
 
-   // Ratios total_probe_length/size for every tested table.
 
-   std::vector<double> single_table_ratios;
 
-   friend ProbeStats operator+(const ProbeStats& a, const ProbeStats& b) {
 
-     ProbeStats res = a;
 
-     res.all_probes_histogram.resize(std::max(res.all_probes_histogram.size(),
 
-                                              b.all_probes_histogram.size()));
 
-     std::transform(b.all_probes_histogram.begin(), b.all_probes_histogram.end(),
 
-                    res.all_probes_histogram.begin(),
 
-                    res.all_probes_histogram.begin(), std::plus<size_t>());
 
-     res.single_table_ratios.insert(res.single_table_ratios.end(),
 
-                                    b.single_table_ratios.begin(),
 
-                                    b.single_table_ratios.end());
 
-     return res;
 
-   }
 
-   // Average ratio total_probe_length/size over tables.
 
-   double AvgRatio() const {
 
-     return std::accumulate(single_table_ratios.begin(),
 
-                            single_table_ratios.end(), 0.0) /
 
-            single_table_ratios.size();
 
-   }
 
-   // Maximum ratio total_probe_length/size over tables.
 
-   double MaxRatio() const {
 
-     return *std::max_element(single_table_ratios.begin(),
 
-                              single_table_ratios.end());
 
-   }
 
-   // Percentile ratio total_probe_length/size over tables.
 
-   double PercentileRatio(double Percentile = 0.95) const {
 
-     auto r = single_table_ratios;
 
-     auto mid = r.begin() + static_cast<size_t>(r.size() * Percentile);
 
-     if (mid != r.end()) {
 
-       std::nth_element(r.begin(), mid, r.end());
 
-       return *mid;
 
-     } else {
 
-       return MaxRatio();
 
-     }
 
-   }
 
-   // Maximum probe length over all elements and all tables.
 
-   size_t MaxProbe() const { return all_probes_histogram.size(); }
 
-   // Fraction of elements with specified probe length.
 
-   std::vector<double> ProbeNormalizedHistogram() const {
 
-     double total_elements = std::accumulate(all_probes_histogram.begin(),
 
-                                             all_probes_histogram.end(), 0ull);
 
-     std::vector<double> res;
 
-     for (size_t p : all_probes_histogram) {
 
-       res.push_back(p / total_elements);
 
-     }
 
-     return res;
 
-   }
 
-   size_t PercentileProbe(double Percentile = 0.99) const {
 
-     size_t idx = 0;
 
-     for (double p : ProbeNormalizedHistogram()) {
 
-       if (Percentile > p) {
 
-         Percentile -= p;
 
-         ++idx;
 
-       } else {
 
-         return idx;
 
-       }
 
-     }
 
-     return idx;
 
-   }
 
-   friend std::ostream& operator<<(std::ostream& out, const ProbeStats& s) {
 
-     out << "{AvgRatio:" << s.AvgRatio() << ", MaxRatio:" << s.MaxRatio()
 
-         << ", PercentileRatio:" << s.PercentileRatio()
 
-         << ", MaxProbe:" << s.MaxProbe() << ", Probes=[";
 
-     for (double p : s.ProbeNormalizedHistogram()) {
 
-       out << p << ",";
 
-     }
 
-     out << "]}";
 
-     return out;
 
-   }
 
- };
 
- struct ExpectedStats {
 
-   double avg_ratio;
 
-   double max_ratio;
 
-   std::vector<std::pair<double, double>> pecentile_ratios;
 
-   std::vector<std::pair<double, double>> pecentile_probes;
 
-   friend std::ostream& operator<<(std::ostream& out, const ExpectedStats& s) {
 
-     out << "{AvgRatio:" << s.avg_ratio << ", MaxRatio:" << s.max_ratio
 
-         << ", PercentileRatios: [";
 
-     for (auto el : s.pecentile_ratios) {
 
-       out << el.first << ":" << el.second << ", ";
 
-     }
 
-     out << "], PercentileProbes: [";
 
-     for (auto el : s.pecentile_probes) {
 
-       out << el.first << ":" << el.second << ", ";
 
-     }
 
-     out << "]}";
 
-     return out;
 
-   }
 
- };
 
- void VerifyStats(size_t size, const ExpectedStats& exp,
 
-                  const ProbeStats& stats) {
 
-   EXPECT_LT(stats.AvgRatio(), exp.avg_ratio) << size << " " << stats;
 
-   EXPECT_LT(stats.MaxRatio(), exp.max_ratio) << size << " " << stats;
 
-   for (auto pr : exp.pecentile_ratios) {
 
-     EXPECT_LE(stats.PercentileRatio(pr.first), pr.second)
 
-         << size << " " << pr.first << " " << stats;
 
-   }
 
-   for (auto pr : exp.pecentile_probes) {
 
-     EXPECT_LE(stats.PercentileProbe(pr.first), pr.second)
 
-         << size << " " << pr.first << " " << stats;
 
-   }
 
- }
 
- using ProbeStatsPerSize = std::map<size_t, ProbeStats>;
 
- // Collect total ProbeStats on num_iters iterations of the following algorithm:
 
- // 1. Create new table and reserve it to keys.size() * 2
 
- // 2. Insert all keys xored with seed
 
- // 3. Collect ProbeStats from final table.
 
- ProbeStats CollectProbeStatsOnKeysXoredWithSeed(const std::vector<int64_t>& keys,
 
-                                                 size_t num_iters) {
 
-   const size_t reserve_size = keys.size() * 2;
 
-   ProbeStats stats;
 
-   int64_t seed = 0x71b1a19b907d6e33;
 
-   while (num_iters--) {
 
-     seed = static_cast<int64_t>(static_cast<uint64_t>(seed) * 17 + 13);
 
-     IntTable t1;
 
-     t1.reserve(reserve_size);
 
-     for (const auto& key : keys) {
 
-       t1.emplace(key ^ seed);
 
-     }
 
-     auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
 
-     stats.all_probes_histogram.resize(
 
-         std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
 
-     std::transform(probe_histogram.begin(), probe_histogram.end(),
 
-                    stats.all_probes_histogram.begin(),
 
-                    stats.all_probes_histogram.begin(), std::plus<size_t>());
 
-     size_t total_probe_seq_length = 0;
 
-     for (size_t i = 0; i < probe_histogram.size(); ++i) {
 
-       total_probe_seq_length += i * probe_histogram[i];
 
-     }
 
-     stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
 
-                                         keys.size());
 
-     t1.erase(t1.begin(), t1.end());
 
-   }
 
-   return stats;
 
- }
 
- ExpectedStats XorSeedExpectedStats() {
 
-   constexpr bool kRandomizesInserts =
 
- #if NDEBUG
 
-       false;
 
- #else   // NDEBUG
 
-       true;
 
- #endif  // NDEBUG
 
-   // The effective load factor is larger in non-opt mode because we insert
 
-   // elements out of order.
 
-   switch (container_internal::Group::kWidth) {
 
-     case 8:
 
-       if (kRandomizesInserts) {
 
-   return {0.05,
 
-           1.0,
 
-           {{0.95, 0.5}},
 
-           {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
 
-       } else {
 
-   return {0.05,
 
-           2.0,
 
-           {{0.95, 0.1}},
 
-           {{0.95, 0}, {0.99, 2}, {0.999, 4}, {0.9999, 10}}};
 
-       }
 
-     case 16:
 
-       if (kRandomizesInserts) {
 
-         return {0.1,
 
-                 1.0,
 
-                 {{0.95, 0.1}},
 
-                 {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
 
-       } else {
 
-         return {0.05,
 
-                 1.0,
 
-                 {{0.95, 0.05}},
 
-                 {{0.95, 0}, {0.99, 1}, {0.999, 4}, {0.9999, 10}}};
 
-       }
 
-   }
 
-   ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
 
-   return {};
 
- }
 
- TEST(Table, DISABLED_EnsureNonQuadraticTopNXorSeedByProbeSeqLength) {
 
-   ProbeStatsPerSize stats;
 
-   std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
 
-   for (size_t size : sizes) {
 
-     stats[size] =
 
-         CollectProbeStatsOnKeysXoredWithSeed(CollectBadMergeKeys(size), 200);
 
-   }
 
-   auto expected = XorSeedExpectedStats();
 
-   for (size_t size : sizes) {
 
-     auto& stat = stats[size];
 
-     VerifyStats(size, expected, stat);
 
-   }
 
- }
 
- // Collect total ProbeStats on num_iters iterations of the following algorithm:
 
- // 1. Create new table
 
- // 2. Select 10% of keys and insert 10 elements key * 17 + j * 13
 
- // 3. Collect ProbeStats from final table
 
- ProbeStats CollectProbeStatsOnLinearlyTransformedKeys(
 
-     const std::vector<int64_t>& keys, size_t num_iters) {
 
-   ProbeStats stats;
 
-   std::random_device rd;
 
-   std::mt19937 rng(rd());
 
-   auto linear_transform = [](size_t x, size_t y) { return x * 17 + y * 13; };
 
-   std::uniform_int_distribution<size_t> dist(0, keys.size()-1);
 
-   while (num_iters--) {
 
-     IntTable t1;
 
-     size_t num_keys = keys.size() / 10;
 
-     size_t start = dist(rng);
 
-     for (size_t i = 0; i != num_keys; ++i) {
 
-       for (size_t j = 0; j != 10; ++j) {
 
-         t1.emplace(linear_transform(keys[(i + start) % keys.size()], j));
 
-       }
 
-     }
 
-     auto probe_histogram = GetHashtableDebugNumProbesHistogram(t1);
 
-     stats.all_probes_histogram.resize(
 
-         std::max(stats.all_probes_histogram.size(), probe_histogram.size()));
 
-     std::transform(probe_histogram.begin(), probe_histogram.end(),
 
-                    stats.all_probes_histogram.begin(),
 
-                    stats.all_probes_histogram.begin(), std::plus<size_t>());
 
-     size_t total_probe_seq_length = 0;
 
-     for (size_t i = 0; i < probe_histogram.size(); ++i) {
 
-       total_probe_seq_length += i * probe_histogram[i];
 
-     }
 
-     stats.single_table_ratios.push_back(total_probe_seq_length * 1.0 /
 
-                                         t1.size());
 
-     t1.erase(t1.begin(), t1.end());
 
-   }
 
-   return stats;
 
- }
 
- ExpectedStats LinearTransformExpectedStats() {
 
-   constexpr bool kRandomizesInserts =
 
- #if NDEBUG
 
-       false;
 
- #else   // NDEBUG
 
-       true;
 
- #endif  // NDEBUG
 
-   // The effective load factor is larger in non-opt mode because we insert
 
-   // elements out of order.
 
-   switch (container_internal::Group::kWidth) {
 
-     case 8:
 
-       if (kRandomizesInserts) {
 
-         return {0.1,
 
-                 0.5,
 
-                 {{0.95, 0.3}},
 
-                 {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
 
-       } else {
 
-         return {0.15,
 
-                 0.5,
 
-                 {{0.95, 0.3}},
 
-                 {{0.95, 0}, {0.99, 3}, {0.999, 15}, {0.9999, 25}}};
 
-       }
 
-     case 16:
 
-       if (kRandomizesInserts) {
 
-         return {0.1,
 
-                 0.4,
 
-                 {{0.95, 0.3}},
 
-                 {{0.95, 0}, {0.99, 1}, {0.999, 8}, {0.9999, 15}}};
 
-       } else {
 
-         return {0.05,
 
-                 0.2,
 
-                 {{0.95, 0.1}},
 
-                 {{0.95, 0}, {0.99, 1}, {0.999, 6}, {0.9999, 10}}};
 
-       }
 
-   }
 
-   ABSL_RAW_LOG(FATAL, "%s", "Unknown Group width");
 
-   return {};
 
- }
 
- TEST(Table, DISABLED_EnsureNonQuadraticTopNLinearTransformByProbeSeqLength) {
 
-   ProbeStatsPerSize stats;
 
-   std::vector<size_t> sizes = {Group::kWidth << 5, Group::kWidth << 10};
 
-   for (size_t size : sizes) {
 
-     stats[size] = CollectProbeStatsOnLinearlyTransformedKeys(
 
-         CollectBadMergeKeys(size), 300);
 
-   }
 
-   auto expected = LinearTransformExpectedStats();
 
-   for (size_t size : sizes) {
 
-     auto& stat = stats[size];
 
-     VerifyStats(size, expected, stat);
 
-   }
 
- }
 
- TEST(Table, EraseCollision) {
 
-   BadTable t;
 
-   // 1 2 3
 
-   t.emplace(1);
 
-   t.emplace(2);
 
-   t.emplace(3);
 
-   EXPECT_THAT(*t.find(1), 1);
 
-   EXPECT_THAT(*t.find(2), 2);
 
-   EXPECT_THAT(*t.find(3), 3);
 
-   EXPECT_EQ(3, t.size());
 
-   // 1 DELETED 3
 
-   t.erase(t.find(2));
 
-   EXPECT_THAT(*t.find(1), 1);
 
-   EXPECT_TRUE(t.find(2) == t.end());
 
-   EXPECT_THAT(*t.find(3), 3);
 
-   EXPECT_EQ(2, t.size());
 
-   // DELETED DELETED 3
 
-   t.erase(t.find(1));
 
-   EXPECT_TRUE(t.find(1) == t.end());
 
-   EXPECT_TRUE(t.find(2) == t.end());
 
-   EXPECT_THAT(*t.find(3), 3);
 
-   EXPECT_EQ(1, t.size());
 
-   // DELETED DELETED DELETED
 
-   t.erase(t.find(3));
 
-   EXPECT_TRUE(t.find(1) == t.end());
 
-   EXPECT_TRUE(t.find(2) == t.end());
 
-   EXPECT_TRUE(t.find(3) == t.end());
 
-   EXPECT_EQ(0, t.size());
 
- }
 
- TEST(Table, EraseInsertProbing) {
 
-   BadTable t(100);
 
-   // 1 2 3 4
 
-   t.emplace(1);
 
-   t.emplace(2);
 
-   t.emplace(3);
 
-   t.emplace(4);
 
-   // 1 DELETED 3 DELETED
 
-   t.erase(t.find(2));
 
-   t.erase(t.find(4));
 
-   // 1 10 3 11 12
 
-   t.emplace(10);
 
-   t.emplace(11);
 
-   t.emplace(12);
 
-   EXPECT_EQ(5, t.size());
 
-   EXPECT_THAT(t, UnorderedElementsAre(1, 10, 3, 11, 12));
 
- }
 
- TEST(Table, Clear) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   t.clear();
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   auto res = t.emplace(0);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_EQ(1, t.size());
 
-   t.clear();
 
-   EXPECT_EQ(0, t.size());
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
- }
 
- TEST(Table, Swap) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   auto res = t.emplace(0);
 
-   EXPECT_TRUE(res.second);
 
-   EXPECT_EQ(1, t.size());
 
-   IntTable u;
 
-   t.swap(u);
 
-   EXPECT_EQ(0, t.size());
 
-   EXPECT_EQ(1, u.size());
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   EXPECT_THAT(*u.find(0), 0);
 
- }
 
- TEST(Table, Rehash) {
 
-   IntTable t;
 
-   EXPECT_TRUE(t.find(0) == t.end());
 
-   t.emplace(0);
 
-   t.emplace(1);
 
-   EXPECT_EQ(2, t.size());
 
-   t.rehash(128);
 
-   EXPECT_EQ(2, t.size());
 
-   EXPECT_THAT(*t.find(0), 0);
 
-   EXPECT_THAT(*t.find(1), 1);
 
- }
 
- TEST(Table, RehashDoesNotRehashWhenNotNecessary) {
 
-   IntTable t;
 
-   t.emplace(0);
 
-   t.emplace(1);
 
-   auto* p = &*t.find(0);
 
-   t.rehash(1);
 
-   EXPECT_EQ(p, &*t.find(0));
 
- }
 
- TEST(Table, RehashZeroDoesNotAllocateOnEmptyTable) {
 
-   IntTable t;
 
-   t.rehash(0);
 
-   EXPECT_EQ(0, t.bucket_count());
 
- }
 
- TEST(Table, RehashZeroDeallocatesEmptyTable) {
 
-   IntTable t;
 
-   t.emplace(0);
 
-   t.clear();
 
-   EXPECT_NE(0, t.bucket_count());
 
-   t.rehash(0);
 
-   EXPECT_EQ(0, t.bucket_count());
 
- }
 
- TEST(Table, RehashZeroForcesRehash) {
 
-   IntTable t;
 
-   t.emplace(0);
 
-   t.emplace(1);
 
-   auto* p = &*t.find(0);
 
-   t.rehash(0);
 
-   EXPECT_NE(p, &*t.find(0));
 
- }
 
- TEST(Table, ConstructFromInitList) {
 
-   using P = std::pair<std::string, std::string>;
 
-   struct Q {
 
-     operator P() const { return {}; }
 
-   };
 
-   StringTable t = {P(), Q(), {}, {{}, {}}};
 
- }
 
- TEST(Table, CopyConstruct) {
 
-   IntTable t;
 
-   t.max_load_factor(.321f);
 
-   t.emplace(0);
 
-   EXPECT_EQ(1, t.size());
 
-   {
 
-     IntTable u(t);
 
-     EXPECT_EQ(1, u.size());
 
-     EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
 
-     EXPECT_THAT(*u.find(0), 0);
 
-   }
 
-   {
 
-     IntTable u{t};
 
-     EXPECT_EQ(1, u.size());
 
-     EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
 
-     EXPECT_THAT(*u.find(0), 0);
 
-   }
 
-   {
 
-     IntTable u = t;
 
-     EXPECT_EQ(1, u.size());
 
-     EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
 
-     EXPECT_THAT(*u.find(0), 0);
 
-   }
 
- }
 
- TEST(Table, CopyConstructWithAlloc) {
 
-   StringTable t;
 
-   t.max_load_factor(.321f);
 
-   t.emplace("a", "b");
 
-   EXPECT_EQ(1, t.size());
 
-   StringTable u(t, Alloc<std::pair<std::string, std::string>>());
 
-   EXPECT_EQ(1, u.size());
 
-   EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
 
-   EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
- }
 
- struct ExplicitAllocIntTable
 
-     : raw_hash_set<IntPolicy, container_internal::hash_default_hash<int64_t>,
 
-                    std::equal_to<int64_t>, Alloc<int64_t>> {
 
-   ExplicitAllocIntTable() {}
 
- };
 
- TEST(Table, AllocWithExplicitCtor) {
 
-   ExplicitAllocIntTable t;
 
-   EXPECT_EQ(0, t.size());
 
- }
 
- TEST(Table, MoveConstruct) {
 
-   {
 
-     StringTable t;
 
-     t.max_load_factor(.321f);
 
-     const float lf = t.max_load_factor();
 
-     t.emplace("a", "b");
 
-     EXPECT_EQ(1, t.size());
 
-     StringTable u(std::move(t));
 
-     EXPECT_EQ(1, u.size());
 
-     EXPECT_EQ(lf, u.max_load_factor());
 
-     EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
-   }
 
-   {
 
-     StringTable t;
 
-     t.max_load_factor(.321f);
 
-     const float lf = t.max_load_factor();
 
-     t.emplace("a", "b");
 
-     EXPECT_EQ(1, t.size());
 
-     StringTable u{std::move(t)};
 
-     EXPECT_EQ(1, u.size());
 
-     EXPECT_EQ(lf, u.max_load_factor());
 
-     EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
-   }
 
-   {
 
-     StringTable t;
 
-     t.max_load_factor(.321f);
 
-     const float lf = t.max_load_factor();
 
-     t.emplace("a", "b");
 
-     EXPECT_EQ(1, t.size());
 
-     StringTable u = std::move(t);
 
-     EXPECT_EQ(1, u.size());
 
-     EXPECT_EQ(lf, u.max_load_factor());
 
-     EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
-   }
 
- }
 
- TEST(Table, MoveConstructWithAlloc) {
 
-   StringTable t;
 
-   t.max_load_factor(.321f);
 
-   const float lf = t.max_load_factor();
 
-   t.emplace("a", "b");
 
-   EXPECT_EQ(1, t.size());
 
-   StringTable u(std::move(t), Alloc<std::pair<std::string, std::string>>());
 
-   EXPECT_EQ(1, u.size());
 
-   EXPECT_EQ(lf, u.max_load_factor());
 
-   EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
- }
 
- TEST(Table, CopyAssign) {
 
-   StringTable t;
 
-   t.max_load_factor(.321f);
 
-   t.emplace("a", "b");
 
-   EXPECT_EQ(1, t.size());
 
-   StringTable u;
 
-   u = t;
 
-   EXPECT_EQ(1, u.size());
 
-   EXPECT_EQ(t.max_load_factor(), u.max_load_factor());
 
-   EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
- }
 
- TEST(Table, CopySelfAssign) {
 
-   StringTable t;
 
-   t.max_load_factor(.321f);
 
-   const float lf = t.max_load_factor();
 
-   t.emplace("a", "b");
 
-   EXPECT_EQ(1, t.size());
 
-   t = *&t;
 
-   EXPECT_EQ(1, t.size());
 
-   EXPECT_EQ(lf, t.max_load_factor());
 
-   EXPECT_THAT(*t.find("a"), Pair("a", "b"));
 
- }
 
- TEST(Table, MoveAssign) {
 
-   StringTable t;
 
-   t.max_load_factor(.321f);
 
-   const float lf = t.max_load_factor();
 
-   t.emplace("a", "b");
 
-   EXPECT_EQ(1, t.size());
 
-   StringTable u;
 
-   u = std::move(t);
 
-   EXPECT_EQ(1, u.size());
 
-   EXPECT_EQ(lf, u.max_load_factor());
 
-   EXPECT_THAT(*u.find("a"), Pair("a", "b"));
 
- }
 
- TEST(Table, Equality) {
 
-   StringTable t;
 
-   std::vector<std::pair<std::string, std::string>> v = {{"a", "b"},
 
-                                                         {"aa", "bb"}};
 
-   t.insert(std::begin(v), std::end(v));
 
-   StringTable u = t;
 
-   EXPECT_EQ(u, t);
 
- }
 
- TEST(Table, Equality2) {
 
-   StringTable t;
 
-   std::vector<std::pair<std::string, std::string>> v1 = {{"a", "b"},
 
-                                                          {"aa", "bb"}};
 
-   t.insert(std::begin(v1), std::end(v1));
 
-   StringTable u;
 
-   std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
 
-                                                          {"aa", "aa"}};
 
-   u.insert(std::begin(v2), std::end(v2));
 
-   EXPECT_NE(u, t);
 
- }
 
- TEST(Table, Equality3) {
 
-   StringTable t;
 
-   std::vector<std::pair<std::string, std::string>> v1 = {{"b", "b"},
 
-                                                          {"bb", "bb"}};
 
-   t.insert(std::begin(v1), std::end(v1));
 
-   StringTable u;
 
-   std::vector<std::pair<std::string, std::string>> v2 = {{"a", "a"},
 
-                                                          {"aa", "aa"}};
 
-   u.insert(std::begin(v2), std::end(v2));
 
-   EXPECT_NE(u, t);
 
- }
 
- TEST(Table, NumDeletedRegression) {
 
-   IntTable t;
 
-   t.emplace(0);
 
-   t.erase(t.find(0));
 
-   // construct over a deleted slot.
 
-   t.emplace(0);
 
-   t.clear();
 
- }
 
- TEST(Table, FindFullDeletedRegression) {
 
-   IntTable t;
 
-   for (int i = 0; i < 1000; ++i) {
 
-     t.emplace(i);
 
-     t.erase(t.find(i));
 
-   }
 
-   EXPECT_EQ(0, t.size());
 
- }
 
- TEST(Table, ReplacingDeletedSlotDoesNotRehash) {
 
-   size_t n;
 
-   {
 
-     // Compute n such that n is the maximum number of elements before rehash.
 
-     IntTable t;
 
-     t.emplace(0);
 
-     size_t c = t.bucket_count();
 
-     for (n = 1; c == t.bucket_count(); ++n) t.emplace(n);
 
-     --n;
 
-   }
 
-   IntTable t;
 
-   t.rehash(n);
 
-   const size_t c = t.bucket_count();
 
-   for (size_t i = 0; i != n; ++i) t.emplace(i);
 
-   EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
 
-   t.erase(0);
 
-   t.emplace(0);
 
-   EXPECT_EQ(c, t.bucket_count()) << "rehashing threshold = " << n;
 
- }
 
- TEST(Table, NoThrowMoveConstruct) {
 
-   ASSERT_TRUE(
 
-       std::is_nothrow_copy_constructible<absl::Hash<absl::string_view>>::value);
 
-   ASSERT_TRUE(std::is_nothrow_copy_constructible<
 
-               std::equal_to<absl::string_view>>::value);
 
-   ASSERT_TRUE(std::is_nothrow_copy_constructible<std::allocator<int>>::value);
 
-   EXPECT_TRUE(std::is_nothrow_move_constructible<StringTable>::value);
 
- }
 
- TEST(Table, NoThrowMoveAssign) {
 
-   ASSERT_TRUE(
 
-       std::is_nothrow_move_assignable<absl::Hash<absl::string_view>>::value);
 
-   ASSERT_TRUE(
 
-       std::is_nothrow_move_assignable<std::equal_to<absl::string_view>>::value);
 
-   ASSERT_TRUE(std::is_nothrow_move_assignable<std::allocator<int>>::value);
 
-   ASSERT_TRUE(
 
-       absl::allocator_traits<std::allocator<int>>::is_always_equal::value);
 
-   EXPECT_TRUE(std::is_nothrow_move_assignable<StringTable>::value);
 
- }
 
- TEST(Table, NoThrowSwappable) {
 
-   ASSERT_TRUE(
 
-       container_internal::IsNoThrowSwappable<absl::Hash<absl::string_view>>());
 
-   ASSERT_TRUE(container_internal::IsNoThrowSwappable<
 
-               std::equal_to<absl::string_view>>());
 
-   ASSERT_TRUE(container_internal::IsNoThrowSwappable<std::allocator<int>>());
 
-   EXPECT_TRUE(container_internal::IsNoThrowSwappable<StringTable>());
 
- }
 
- TEST(Table, HeterogeneousLookup) {
 
-   struct Hash {
 
-     size_t operator()(int64_t i) const { return i; }
 
-     size_t operator()(double i) const {
 
-       ADD_FAILURE();
 
-       return i;
 
-     }
 
-   };
 
-   struct Eq {
 
-     bool operator()(int64_t a, int64_t b) const { return a == b; }
 
-     bool operator()(double a, int64_t b) const {
 
-       ADD_FAILURE();
 
-       return a == b;
 
-     }
 
-     bool operator()(int64_t a, double b) const {
 
-       ADD_FAILURE();
 
-       return a == b;
 
-     }
 
-     bool operator()(double a, double b) const {
 
-       ADD_FAILURE();
 
-       return a == b;
 
-     }
 
-   };
 
-   struct THash {
 
-     using is_transparent = void;
 
-     size_t operator()(int64_t i) const { return i; }
 
-     size_t operator()(double i) const { return i; }
 
-   };
 
-   struct TEq {
 
-     using is_transparent = void;
 
-     bool operator()(int64_t a, int64_t b) const { return a == b; }
 
-     bool operator()(double a, int64_t b) const { return a == b; }
 
-     bool operator()(int64_t a, double b) const { return a == b; }
 
-     bool operator()(double a, double b) const { return a == b; }
 
-   };
 
-   raw_hash_set<IntPolicy, Hash, Eq, Alloc<int64_t>> s{0, 1, 2};
 
-   // It will convert to int64_t before the query.
 
-   EXPECT_EQ(1, *s.find(double{1.1}));
 
-   raw_hash_set<IntPolicy, THash, TEq, Alloc<int64_t>> ts{0, 1, 2};
 
-   // It will try to use the double, and fail to find the object.
 
-   EXPECT_TRUE(ts.find(1.1) == ts.end());
 
- }
 
- template <class Table>
 
- using CallFind = decltype(std::declval<Table&>().find(17));
 
- template <class Table>
 
- using CallErase = decltype(std::declval<Table&>().erase(17));
 
- template <class Table>
 
- using CallExtract = decltype(std::declval<Table&>().extract(17));
 
- template <class Table>
 
- using CallPrefetch = decltype(std::declval<Table&>().prefetch(17));
 
- template <class Table>
 
- using CallCount = decltype(std::declval<Table&>().count(17));
 
- template <template <typename> class C, class Table, class = void>
 
- struct VerifyResultOf : std::false_type {};
 
- template <template <typename> class C, class Table>
 
- struct VerifyResultOf<C, Table, absl::void_t<C<Table>>> : std::true_type {};
 
- TEST(Table, HeterogeneousLookupOverloads) {
 
-   using NonTransparentTable =
 
-       raw_hash_set<StringPolicy, absl::Hash<absl::string_view>,
 
-                    std::equal_to<absl::string_view>, std::allocator<int>>;
 
-   EXPECT_FALSE((VerifyResultOf<CallFind, NonTransparentTable>()));
 
-   EXPECT_FALSE((VerifyResultOf<CallErase, NonTransparentTable>()));
 
-   EXPECT_FALSE((VerifyResultOf<CallExtract, NonTransparentTable>()));
 
-   EXPECT_FALSE((VerifyResultOf<CallPrefetch, NonTransparentTable>()));
 
-   EXPECT_FALSE((VerifyResultOf<CallCount, NonTransparentTable>()));
 
-   using TransparentTable = raw_hash_set<
 
-       StringPolicy,
 
-       absl::container_internal::hash_default_hash<absl::string_view>,
 
-       absl::container_internal::hash_default_eq<absl::string_view>,
 
-       std::allocator<int>>;
 
-   EXPECT_TRUE((VerifyResultOf<CallFind, TransparentTable>()));
 
-   EXPECT_TRUE((VerifyResultOf<CallErase, TransparentTable>()));
 
-   EXPECT_TRUE((VerifyResultOf<CallExtract, TransparentTable>()));
 
-   EXPECT_TRUE((VerifyResultOf<CallPrefetch, TransparentTable>()));
 
-   EXPECT_TRUE((VerifyResultOf<CallCount, TransparentTable>()));
 
- }
 
- // TODO(alkis): Expand iterator tests.
 
- TEST(Iterator, IsDefaultConstructible) {
 
-   StringTable::iterator i;
 
-   EXPECT_TRUE(i == StringTable::iterator());
 
- }
 
- TEST(ConstIterator, IsDefaultConstructible) {
 
-   StringTable::const_iterator i;
 
-   EXPECT_TRUE(i == StringTable::const_iterator());
 
- }
 
- TEST(Iterator, ConvertsToConstIterator) {
 
-   StringTable::iterator i;
 
-   EXPECT_TRUE(i == StringTable::const_iterator());
 
- }
 
- TEST(Iterator, Iterates) {
 
-   IntTable t;
 
-   for (size_t i = 3; i != 6; ++i) EXPECT_TRUE(t.emplace(i).second);
 
-   EXPECT_THAT(t, UnorderedElementsAre(3, 4, 5));
 
- }
 
- TEST(Table, Merge) {
 
-   StringTable t1, t2;
 
-   t1.emplace("0", "-0");
 
-   t1.emplace("1", "-1");
 
-   t2.emplace("0", "~0");
 
-   t2.emplace("2", "~2");
 
-   EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1")));
 
-   EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0"), Pair("2", "~2")));
 
-   t1.merge(t2);
 
-   EXPECT_THAT(t1, UnorderedElementsAre(Pair("0", "-0"), Pair("1", "-1"),
 
-                                        Pair("2", "~2")));
 
-   EXPECT_THAT(t2, UnorderedElementsAre(Pair("0", "~0")));
 
- }
 
- TEST(Nodes, EmptyNodeType) {
 
-   using node_type = StringTable::node_type;
 
-   node_type n;
 
-   EXPECT_FALSE(n);
 
-   EXPECT_TRUE(n.empty());
 
-   EXPECT_TRUE((std::is_same<node_type::allocator_type,
 
-                             StringTable::allocator_type>::value));
 
- }
 
- TEST(Nodes, ExtractInsert) {
 
-   constexpr char k0[] = "Very long std::string zero.";
 
-   constexpr char k1[] = "Very long std::string one.";
 
-   constexpr char k2[] = "Very long std::string two.";
 
-   StringTable t = {{k0, ""}, {k1, ""}, {k2, ""}};
 
-   EXPECT_THAT(t,
 
-               UnorderedElementsAre(Pair(k0, ""), Pair(k1, ""), Pair(k2, "")));
 
-   auto node = t.extract(k0);
 
-   EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
 
-   EXPECT_TRUE(node);
 
-   EXPECT_FALSE(node.empty());
 
-   StringTable t2;
 
-   StringTable::insert_return_type res = t2.insert(std::move(node));
 
-   EXPECT_TRUE(res.inserted);
 
-   EXPECT_THAT(*res.position, Pair(k0, ""));
 
-   EXPECT_FALSE(res.node);
 
-   EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
 
-   // Not there.
 
-   EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
 
-   node = t.extract("Not there!");
 
-   EXPECT_THAT(t, UnorderedElementsAre(Pair(k1, ""), Pair(k2, "")));
 
-   EXPECT_FALSE(node);
 
-   // Inserting nothing.
 
-   res = t2.insert(std::move(node));
 
-   EXPECT_FALSE(res.inserted);
 
-   EXPECT_EQ(res.position, t2.end());
 
-   EXPECT_FALSE(res.node);
 
-   EXPECT_THAT(t2, UnorderedElementsAre(Pair(k0, "")));
 
-   t.emplace(k0, "1");
 
-   node = t.extract(k0);
 
-   // Insert duplicate.
 
-   res = t2.insert(std::move(node));
 
-   EXPECT_FALSE(res.inserted);
 
-   EXPECT_THAT(*res.position, Pair(k0, ""));
 
-   EXPECT_TRUE(res.node);
 
-   EXPECT_FALSE(node);
 
- }
 
- IntTable MakeSimpleTable(size_t size) {
 
-   IntTable t;
 
-   while (t.size() < size) t.insert(t.size());
 
-   return t;
 
- }
 
- std::vector<int> OrderOfIteration(const IntTable& t) {
 
-   return {t.begin(), t.end()};
 
- }
 
- // These IterationOrderChanges tests depend on non-deterministic behavior.
 
- // We are injecting non-determinism from the pointer of the table, but do so in
 
- // a way that only the page matters. We have to retry enough times to make sure
 
- // we are touching different memory pages to cause the ordering to change.
 
- // We also need to keep the old tables around to avoid getting the same memory
 
- // blocks over and over.
 
- TEST(Table, IterationOrderChangesByInstance) {
 
-   for (size_t size : {2, 6, 12, 20}) {
 
-     const auto reference_table = MakeSimpleTable(size);
 
-     const auto reference = OrderOfIteration(reference_table);
 
-     std::vector<IntTable> tables;
 
-     bool found_difference = false;
 
-     for (int i = 0; !found_difference && i < 500; ++i) {
 
-       tables.push_back(MakeSimpleTable(size));
 
-       found_difference = OrderOfIteration(tables.back()) != reference;
 
-     }
 
-     if (!found_difference) {
 
-       FAIL()
 
-           << "Iteration order remained the same across many attempts with size "
 
-           << size;
 
-     }
 
-   }
 
- }
 
- TEST(Table, IterationOrderChangesOnRehash) {
 
-   std::vector<IntTable> garbage;
 
-   for (int i = 0; i < 500; ++i) {
 
-     auto t = MakeSimpleTable(20);
 
-     const auto reference = OrderOfIteration(t);
 
-     // Force rehash to the same size.
 
-     t.rehash(0);
 
-     auto trial = OrderOfIteration(t);
 
-     if (trial != reference) {
 
-       // We are done.
 
-       return;
 
-     }
 
-     garbage.push_back(std::move(t));
 
-   }
 
-   FAIL() << "Iteration order remained the same across many attempts.";
 
- }
 
- // Verify that pointers are invalidated as soon as a second element is inserted.
 
- // This prevents dependency on pointer stability on small tables.
 
- TEST(Table, UnstablePointers) {
 
-   IntTable table;
 
-   const auto addr = [&](int i) {
 
-     return reinterpret_cast<uintptr_t>(&*table.find(i));
 
-   };
 
-   table.insert(0);
 
-   const uintptr_t old_ptr = addr(0);
 
-   // This causes a rehash.
 
-   table.insert(1);
 
-   EXPECT_NE(old_ptr, addr(0));
 
- }
 
- // Confirm that we assert if we try to erase() end().
 
- TEST(TableDeathTest, EraseOfEndAsserts) {
 
-   // Use an assert with side-effects to figure out if they are actually enabled.
 
-   bool assert_enabled = false;
 
-   assert([&]() {
 
-     assert_enabled = true;
 
-     return true;
 
-   }());
 
-   if (!assert_enabled) return;
 
-   IntTable t;
 
-   // Extra simple "regexp" as regexp support is highly varied across platforms.
 
-   constexpr char kDeathMsg[] = "it != end";
 
-   EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg);
 
- }
 
- TEST(RawHashSamplerTest, Sample) {
 
-   // Enable the feature even if the prod default is off.
 
-   SetHashtablezEnabled(true);
 
-   SetHashtablezSampleParameter(100);
 
-   auto& sampler = HashtablezSampler::Global();
 
-   size_t start_size = 0;
 
-   start_size += sampler.Iterate([&](const HashtablezInfo&) { ++start_size; });
 
-   std::vector<IntTable> tables;
 
-   for (int i = 0; i < 1000000; ++i) {
 
-     tables.emplace_back();
 
-     tables.back().insert(1);
 
-   }
 
-   size_t end_size = 0;
 
-   end_size += sampler.Iterate([&](const HashtablezInfo&) { ++end_size; });
 
-   EXPECT_NEAR((end_size - start_size) / static_cast<double>(tables.size()),
 
-               0.01, 0.005);
 
- }
 
- #ifdef ADDRESS_SANITIZER
 
- TEST(Sanitizer, PoisoningUnused) {
 
-   IntTable t;
 
-   t.reserve(5);
 
-   // Insert something to force an allocation.
 
-   int64_t& v1 = *t.insert(0).first;
 
-   // Make sure there is something to test.
 
-   ASSERT_GT(t.capacity(), 1);
 
-   int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t);
 
-   for (size_t i = 0; i < t.capacity(); ++i) {
 
-     EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i));
 
-   }
 
- }
 
- TEST(Sanitizer, PoisoningOnErase) {
 
-   IntTable t;
 
-   int64_t& v = *t.insert(0).first;
 
-   EXPECT_FALSE(__asan_address_is_poisoned(&v));
 
-   t.erase(0);
 
-   EXPECT_TRUE(__asan_address_is_poisoned(&v));
 
- }
 
- #endif  // ADDRESS_SANITIZER
 
- }  // namespace
 
- }  // namespace container_internal
 
- }  // namespace absl
 
 
  |