// Copyright 2022 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #if defined(BOOST_MSVC) // conditional expression is constant #pragma warning(disable : 4127) #endif void macros_test() { if (std::numeric_limits::digits >= 64) { #if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) BOOST_ERROR("std::numeric_limits::digits >= 64, but " "BOOST_UNORDERED_FCA_HAS_64B_SIZE_T is not defined"); #endif } else { #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) BOOST_ERROR("std::numeric_limits::digits < 64, but " "BOOST_UNORDERED_FCA_HAS_64B_SIZE_T is defined"); #endif } } // Pretty inefficient, but the test is fast enough. // Might be too slow if we had larger primes? bool is_prime(std::size_t x) { if (x == 2) { return true; } if (x == 1 || x % 2 == 0) { return false; } // y*y <= x is susceptible to overflow, so instead make sure to use y <= (x/y) for (std::size_t y = 3; y <= (x / y); y += 2) { if (x % y == 0) { return false; } } return true; } void prime_sizes_test() { // just some basic sanity checks // BOOST_TEST(!is_prime(0)); BOOST_TEST(!is_prime(1)); BOOST_TEST(is_prime(2)); BOOST_TEST(is_prime(3)); BOOST_TEST(is_prime(13)); BOOST_TEST(!is_prime(4)); BOOST_TEST(!is_prime(100)); BOOST_TEST(!is_prime(49)); std::size_t const* sizes = boost::unordered::detail::prime_fmod_size<>::sizes; std::size_t sizes_len = boost::unordered::detail::prime_fmod_size<>::sizes_len; // prove every number in our sizes array is prime // BOOST_TEST_GT(sizes_len, 0u); for (std::size_t i = 0; i < sizes_len; ++i) { BOOST_TEST(is_prime(sizes[i])); } // prove that every subsequent number in the sequence is larger than the // previous // for (std::size_t i = 1; i < sizes_len; ++i) { BOOST_TEST_GT(sizes[i], sizes[i - 1]); } #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) // now we wish to prove that if we do have the reciprocals stored, we have the // correct amount of them, i.e. one for every entry in sizes[] that fits in 32 // bits // boost::uint64_t const* inv_sizes32 = boost::unordered::detail::prime_fmod_size<>::inv_sizes32; std::size_t inv_sizes32_len = boost::unordered::detail::prime_fmod_size<>::inv_sizes32_len; std::size_t count = 0; for (std::size_t i = 0; i < sizes_len; ++i) { if (sizes[i] <= UINT32_MAX) { ++count; } } BOOST_TEST_GT(inv_sizes32_len, 0u); BOOST_TEST_EQ(inv_sizes32_len, count); // these values should also be monotonically decreasing // for (std::size_t i = 1; i < inv_sizes32_len; ++i) { BOOST_TEST_LT(inv_sizes32[i], inv_sizes32[i - 1]); } // now make sure the values in inv_sizes32 are what they should be as derived // from the paper // for (std::size_t i = 0; i < inv_sizes32_len; ++i) { std::size_t const size = sizes[i]; BOOST_TEST_LE(size, UINT_MAX); boost::uint32_t d = static_cast(sizes[i]); boost::uint64_t M = ((boost::ulong_long_type(0xffffffff) << 32) + boost::ulong_long_type(0xffffffff)) / d + 1; BOOST_TEST_EQ(inv_sizes32[i], M); } #endif } void get_remainder_test() { #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) struct { // boost::unordered::detail::prime_fmod_size<>::get_remainder // uses several internal implementations depending on the availability of // certain intrinsics or 128 bit integer support, defaulting to a slow, // portable routine. The following is a transcription of the portable // routine used here for verification purposes. // boost::uint64_t operator()(boost::uint64_t f, boost::uint32_t d) { boost::uint64_t r1 = (f & UINT32_MAX) * d; boost::uint64_t r2 = (f >> 32) * d; r2 += r1 >> 32; return r2 >> 32; } } get_remainder; boost::detail::splitmix64 rng; for (std::size_t i = 0; i < 1000000u; ++i) { boost::uint64_t f = rng(); boost::uint32_t d = rng() & 0xffffffffu; boost::uint64_t r1 = boost::unordered::detail::prime_fmod_size<>::get_remainder(f, d); boost::uint64_t r2 = get_remainder(f, d); if (!BOOST_TEST_EQ(r1, r2)) { std::cerr << "f: " << f << ", d: " << d << std::endl; return; } } #endif } void modulo_test() { std::size_t const* sizes = boost::unordered::detail::prime_fmod_size<>::sizes; std::size_t const sizes_len = boost::unordered::detail::prime_fmod_size<>::sizes_len; boost::detail::splitmix64 rng; for (std::size_t i = 0; i < 1000000u; ++i) { std::size_t hash = static_cast(-1) & rng(); for (std::size_t j = 0; j < sizes_len; ++j) { std::size_t h = hash; #if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) if (sizes[j] <= UINT_MAX) { h = boost::uint32_t(h & 0xffffffffu) + boost::uint32_t(h >> 32); } #endif std::size_t p1 = boost::unordered::detail::prime_fmod_size<>::position(hash, j); std::size_t p2 = h % sizes[j]; if (!BOOST_TEST_EQ(p1, p2)) { std::cerr << "hash: " << hash << ", j: " << j << ", h: " << h << ", sizes[" << j << "]: " << sizes[j] << std::endl; return; } } } } int main() { macros_test(); prime_sizes_test(); get_remainder_test(); modulo_test(); return boost::report_errors(); }