mirror of
https://github.com/Xahau/xahaud.git
synced 2026-09-25 14:50:17 +00:00
fix(basics): allow zero-bit groups in random word extraction
This commit is contained in:
@@ -58,11 +58,14 @@ std::uint64_t
|
||||
randomU64(Engine& engine)
|
||||
{
|
||||
static_assert(std::is_unsigned_v<typename Engine::result_type>);
|
||||
static_assert(
|
||||
std::numeric_limits<typename Engine::result_type>::digits <= 64);
|
||||
static_assert(Engine::min() < Engine::max());
|
||||
auto const draw = [&engine]() -> std::uint64_t {
|
||||
return static_cast<std::uint64_t>(engine() - Engine::min());
|
||||
};
|
||||
|
||||
// 0 means the span is 2^64. That is one full-range draw.
|
||||
// A wrapped cardinality of 0 means 2^64 values: one full-range draw.
|
||||
constexpr auto span =
|
||||
static_cast<std::uint64_t>(Engine::max() - Engine::min());
|
||||
constexpr std::uint64_t range = span + 1u;
|
||||
@@ -101,7 +104,9 @@ randomU64(Engine& engine)
|
||||
constexpr auto y0 = (std::uint64_t{1} << w0) * (range >> w0);
|
||||
constexpr auto y1 =
|
||||
(std::uint64_t{1} << (w0 + 1)) * (range >> (w0 + 1));
|
||||
static_assert(w0 > 0 && w0 < 63);
|
||||
// R == 3 gives n == 65 and w0 == 0. The first group still
|
||||
// consumes its draw even though it contributes no output bits.
|
||||
static_assert(w0 >= 0 && w0 < 63);
|
||||
|
||||
std::uint64_t word = 0;
|
||||
for (int k = 0; k != n0; ++k)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
namespace ripple {
|
||||
@@ -95,6 +96,33 @@ struct NonzeroMin
|
||||
}
|
||||
};
|
||||
|
||||
// A valid three-value engine exercises the zero-bit first group in
|
||||
// independent_bits_engine. Cycling values make draw consumption explicit.
|
||||
template <class Result, Result Minimum = 0>
|
||||
struct ThreeValues
|
||||
{
|
||||
using result_type = Result;
|
||||
std::size_t calls = 0;
|
||||
|
||||
static constexpr result_type
|
||||
min()
|
||||
{
|
||||
return Minimum;
|
||||
}
|
||||
|
||||
static constexpr result_type
|
||||
max()
|
||||
{
|
||||
return Minimum + 2;
|
||||
}
|
||||
|
||||
result_type
|
||||
operator()()
|
||||
{
|
||||
return static_cast<result_type>(Minimum + (calls++ % 3));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class random_test : public beast::unit_test::suite
|
||||
@@ -185,6 +213,54 @@ public:
|
||||
BEAST_EXPECT(detail::randomU64(uneven) == ibits());
|
||||
}
|
||||
|
||||
void
|
||||
testZeroBitGroup()
|
||||
{
|
||||
testcase("three-value engines retain the zero-bit group draw");
|
||||
auto const check = [&](auto engine) {
|
||||
using Engine = decltype(engine);
|
||||
std::independent_bits_engine<Engine, 64, std::uint64_t> reference{
|
||||
engine};
|
||||
|
||||
// R=3: n=65, w0=0, n0=1, y0=3, y1=2. Discard the
|
||||
// first draw, then take 64 bits, rejecting normalized value 2.
|
||||
// For this cycle the accepted bits are 1010...10 (97 draws).
|
||||
auto const first = detail::randomU64(engine);
|
||||
BEAST_EXPECT(first == 0xaaaaaaaaaaaaaaaaULL);
|
||||
BEAST_EXPECT(engine.calls == 97);
|
||||
BEAST_EXPECT(first == reference());
|
||||
BEAST_EXPECT(engine.calls == reference.base().calls);
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
BEAST_EXPECT(detail::randomU64(engine) == reference());
|
||||
BEAST_EXPECT(engine.calls == reference.base().calls);
|
||||
}
|
||||
|
||||
// Also exercise the public closed-range mapper with the same
|
||||
// normalized stream, including the runtime fault-hook range.
|
||||
for (auto const count : {10u, 10'000u})
|
||||
{
|
||||
auto const n = static_cast<std::uint64_t>(count);
|
||||
auto const slack = static_cast<std::uint64_t>(-n) % n;
|
||||
std::uint64_t expected;
|
||||
do
|
||||
{
|
||||
expected = reference();
|
||||
} while (expected < slack);
|
||||
BEAST_EXPECT(rand_int(engine, 0u, count - 1) == expected % n);
|
||||
BEAST_EXPECT(engine.calls == reference.base().calls);
|
||||
}
|
||||
};
|
||||
|
||||
check(ThreeValues<std::uint32_t>{});
|
||||
check(ThreeValues<std::uint64_t>{});
|
||||
check(ThreeValues<std::uint32_t, 5>{});
|
||||
check(ThreeValues<
|
||||
std::uint64_t,
|
||||
std::numeric_limits<std::uint64_t>::max() - 2>{});
|
||||
}
|
||||
|
||||
void
|
||||
testBounds()
|
||||
{
|
||||
@@ -212,6 +288,7 @@ public:
|
||||
{
|
||||
testReducedMapping();
|
||||
testEngineVectors();
|
||||
testZeroBitGroup();
|
||||
testBounds();
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user