+namespace {
+
+uint8_t slowDefaultNumLowerBits(size_t upperBound, size_t size) {
+ if (size == 0 || upperBound < size) {
+ return 0;
+ }
+ // floor(log(upperBound / size));
+ return uint8_t(folly::findLastSet(upperBound / size) - 1);
+}
+
+} // namespace
+
+TEST(EliasFanoCoding, defaultNumLowerBits) {
+ // Verify that slowDefaultNumLowerBits and optimized
+ // Encoder::defaultNumLowerBits agree.
+ constexpr size_t kNumIterations = 2500;
+ auto compare = [](size_t upperBound, size_t size) {
+ using Encoder = EliasFanoEncoderV2<size_t>;
+ EXPECT_EQ(int(slowDefaultNumLowerBits(upperBound, size)),
+ int(Encoder::defaultNumLowerBits(upperBound, size)))
+ << upperBound << " " << size;
+ };
+ auto batch = [&compare](size_t initialUpperBound) {
+ for (size_t upperBound = initialUpperBound, i = 0;
+ i < kNumIterations;
+ ++i, --upperBound) {
+ // Test "size" values close to "upperBound".
+ for (size_t size = upperBound, j = 0; j < kNumIterations; ++j, --size) {
+ compare(upperBound, size);
+ }
+ // Sample "size" values between [0, upperBound].
+ for (size_t size = upperBound;
+ size > 1 + upperBound / kNumIterations;
+ size -= 1 + upperBound / kNumIterations) {
+ compare(upperBound, size);
+ }
+ // Test "size" values close to 0.
+ for (size_t size = 0; size < kNumIterations; ++size) {
+ compare(upperBound, size);
+ }
+ }
+ };
+ batch(std::numeric_limits<size_t>::max());
+ batch(kNumIterations + 1312213123);
+ batch(kNumIterations);
+
+ std::mt19937 gen;
+ std::uniform_int_distribution<size_t> distribution;
+ for (size_t i = 0; i < kNumIterations; ++i) {
+ const auto a = distribution(gen);
+ const auto b = distribution(gen);
+ compare(std::max(a, b), std::min(a, b));
+ }
+}
+