Update FuzzingBitGen to reduce possible infinite loops.

The old FuzzingBitGen algorithm would pull a byte from the head of the data stream to determine whether to return a min or a max, and then it would attempt to pull variate data from the tail of the stream. Once the data stream was expired it would return minimum values from the distributions, which could lead to infinite loops in rejection sampling algorithms.

The updated FuzzingBitGen now takes the following.
  1. Separate control and a data streams (as absl::Span<uint8_t>).
     The control stream indicates whether the distribution functions will return boundary values (min, max, mean) or a value derived from the data stream.
     The data stream provides the actual byte data for generating random values.

  2. A seed for an internal LCG PRNG (as uint64_t).
     When the data stream is exhausted, FuzzingBitGen uses the internal LCG to generate random variates. While the old version had an internal LCG PRNG, those values were not used by the distribution functions.

The basic flow of each variate generation is:
  1. Read a byte from the control stream (in fuzzing_bit_gen).
  2. Depending on the byte, return a min/max/mean/variate, etc.
  3. Once the data stream is expired, use an internal LCG to generate variates.

This update also calls c++ distribution functions in more cases, so that outputs are more aligned with actual distribution behavior.

Adds a test to demonstrate that std::shuffle() is properly manipulated by the fuzzing framework.
Also add additional tests to FuzzingBitGen for the distribution functions.

NOTE: This will change the variates generated by FuzzingBitGen from prior versions.
PiperOrigin-RevId: 885638015
diff --git a/domain_tests/BUILD b/domain_tests/BUILD
index df77b1f..8d8d251 100644
--- a/domain_tests/BUILD
+++ b/domain_tests/BUILD
@@ -103,9 +103,11 @@
     srcs = ["bitgen_ref_domain_test.cc"],
     deps = [
         ":domain_testing",
+        "@abseil-cpp//absl/container:flat_hash_set",
         "@abseil-cpp//absl/random",
         "@abseil-cpp//absl/random:bit_gen_ref",
         "@com_google_fuzztest//fuzztest:domain_core",
+        "@com_google_fuzztest//fuzztest/internal:printer",
         "@googletest//:gtest_main",
     ],
 )
diff --git a/domain_tests/CMakeLists.txt b/domain_tests/CMakeLists.txt
index 9a40b67..6eac153 100644
--- a/domain_tests/CMakeLists.txt
+++ b/domain_tests/CMakeLists.txt
@@ -82,6 +82,22 @@
 
 fuzztest_cc_test(
   NAME
+    bitgen_ref_domain_test
+  SRCS
+    "bitgen_ref_domain_test.cc"
+  DEPS
+    fuzztest::domain_testing
+    absl::flat_hash_set
+    absl::random_bit_gen_ref
+    absl::random_random
+    fuzztest::domain_core
+    fuzztest::meta
+    fuzztest::printer
+    GTest::gmock_main
+)
+
+fuzztest_cc_test(
+  NAME
     container_combinators_test
   SRCS
     "container_combinators_test.cc"
diff --git a/domain_tests/bitgen_ref_domain_test.cc b/domain_tests/bitgen_ref_domain_test.cc
index 869bc0f..285d6b2 100644
--- a/domain_tests/bitgen_ref_domain_test.cc
+++ b/domain_tests/bitgen_ref_domain_test.cc
@@ -12,13 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <string>
 #include <vector>
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/random/bit_gen_ref.h"
 #include "absl/random/random.h"
 #include "./fuzztest/domain_core.h"
 #include "./domain_tests/domain_testing.h"
+#include "./fuzztest/internal/printer.h"
 
 namespace fuzztest {
 namespace {
@@ -27,35 +31,52 @@
   absl::BitGen bitgen_for_seeding;
 
   Domain<absl::BitGenRef> domain = Arbitrary<absl::BitGenRef>();
-  Value v0(domain, bitgen_for_seeding);
-  Value v1(domain, bitgen_for_seeding);
 
-  // Discard the first value, which may be from the data stream.
-  // If the implementation of BitGenRefDomain changes this may break.
-  v0.user_value();
-  v1.user_value();
-
-  std::vector<absl::BitGenRef::result_type> a, b;
+  // Initialize a set of bitgen references.
+  std::vector<decltype(Value(domain, bitgen_for_seeding))> values;
+  values.reserve(10);
   for (int i = 0; i < 10; ++i) {
-    a.push_back(v0.user_value());
-    b.push_back(v1.user_value());
+    values.emplace_back(domain, bitgen_for_seeding);
   }
-  EXPECT_NE(a, b);
+
+  // Some of the "randomly initialized" streams should be different.
+  for (int i = 0; i < 10; ++i) {
+    absl::flat_hash_set<absl::BitGenRef::result_type> s;
+    for (auto& v : values) {
+      s.insert(v.user_value());
+    }
+    EXPECT_NE(s.size(), 1) << *s.begin();
+  }
 }
 
-TEST(BitGenRefDomainTest, AbseilUniformReturnsLowerBoundWhenExhausted) {
+TEST(BitGenRefDomainTest, AbseilUniformIsFunctionalWhenExhausted) {
+  absl::BitGen bitgen_for_seeding;
+
+  Domain<absl::BitGenRef> domain = Arbitrary<absl::BitGenRef>();
+  Value v0(domain, bitgen_for_seeding);
+  Value v1(v0, domain);
+
+  // When the same domain is used to generate multiple values, the generated
+  // data sequence should be identical.
+  std::vector<int> values;
+  for (int i = 0; i < 100; ++i) {
+    EXPECT_EQ(absl::Uniform<int>(v0.user_value, 0, 100),
+              absl::Uniform<int>(v1.user_value, 0, 100))
+        << i;
+  }
+}
+
+TEST(BitGenRefDomainTest, IsPrintable) {
   absl::BitGen bitgen_for_seeding;
 
   Domain<absl::BitGenRef> domain = Arbitrary<absl::BitGenRef>();
   Value v0(domain, bitgen_for_seeding);
 
-  // Discard the first value, which may be from the data stream.
-  // If the implementation of BitGenRefDomain changes this may break.
-  v0.user_value();
-
-  for (int i = 0; i < 10; ++i) {
-    EXPECT_EQ(absl::Uniform<int>(v0.user_value, 0, 100), 0);
-  }
+  // Print corpus value
+  std::string s;
+  domain.GetPrinter().PrintCorpusValue(
+      v0.corpus_value, &s, domain_implementor::PrintMode::kHumanReadable);
+  EXPECT_THAT(s, testing::StartsWith("FuzzingBitGen"));
 }
 
 }  // namespace
diff --git a/e2e_tests/functional_test.cc b/e2e_tests/functional_test.cc
index bd54ea7..b46d272 100644
--- a/e2e_tests/functional_test.cc
+++ b/e2e_tests/functional_test.cc
@@ -1703,7 +1703,16 @@
 
 TEST_P(FuzzingModeCrashFindingTest, BitGenRefTestFindsAbortInFuzzingMode) {
   auto [status, std_out, std_err] = Run("MySuite.BitGenRef");
-  EXPECT_THAT_LOG(std_err, HasSubstr("argument 0: absl::BitGenRef{}"));
+  EXPECT_THAT_LOG(std_err, AnyOf(HasSubstr("argument 0: absl::BitGenRef{}"),
+                                 HasSubstr("argument 0: FuzzingBitGen")));
+  ExpectTargetAbort(status, std_err);
+}
+
+TEST_P(FuzzingModeCrashFindingTest,
+       BitGenRefShuffleTestFindsAbortInFuzzingMode) {
+  auto [status, std_out, std_err] = Run("MySuite.BitGenRefShuffle");
+  EXPECT_THAT_LOG(std_err, AnyOf(HasSubstr("argument 0: absl::BitGenRef{}"),
+                                 HasSubstr("argument 0: FuzzingBitGen")));
   ExpectTargetAbort(status, std_err);
 }
 
diff --git a/e2e_tests/testdata/BUILD b/e2e_tests/testdata/BUILD
index 8ee022a..0db3a80 100644
--- a/e2e_tests/testdata/BUILD
+++ b/e2e_tests/testdata/BUILD
@@ -33,6 +33,7 @@
     testonly = 1,
     srcs = ["fuzz_tests_for_microbenchmarking.cc"],
     deps = [
+        "@abseil-cpp//absl/container:flat_hash_set",
         "@abseil-cpp//absl/random:bit_gen_ref",
         "@abseil-cpp//absl/random:distributions",
         "@abseil-cpp//absl/strings:str_format",
diff --git a/e2e_tests/testdata/CMakeLists.txt b/e2e_tests/testdata/CMakeLists.txt
index a6c188a..ec93a80 100644
--- a/e2e_tests/testdata/CMakeLists.txt
+++ b/e2e_tests/testdata/CMakeLists.txt
@@ -10,6 +10,7 @@
   fuzztest::test_protobuf_cc_proto
   absl::random_bit_gen_ref
   absl::random_distributions
+  absl::flat_hash_set
   absl::str_format
   absl::string_view
 )
diff --git a/e2e_tests/testdata/fuzz_tests_for_microbenchmarking.cc b/e2e_tests/testdata/fuzz_tests_for_microbenchmarking.cc
index f1c7d63..c0fcc1c 100644
--- a/e2e_tests/testdata/fuzz_tests_for_microbenchmarking.cc
+++ b/e2e_tests/testdata/fuzz_tests_for_microbenchmarking.cc
@@ -24,6 +24,7 @@
 // i.e., to check that the fuzzer behaves as expected and outputs the expected
 // results. E.g., the fuzzer finds the abort() or bug.
 
+#include <algorithm>
 #include <array>
 #include <cmath>
 #include <cstdint>
@@ -33,10 +34,12 @@
 #include <string>
 #include <string_view>
 #include <tuple>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "./fuzztest/fuzztest.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/random/bit_gen_ref.h"
 #include "absl/random/distributions.h"
 #include "absl/strings/str_format.h"
@@ -240,15 +243,32 @@
     .WithDomains(fuzztest::VectorOf(fuzztest::Arbitrary<char>()).WithSize(4));
 
 __attribute__((optnone)) void BitGenRef(absl::BitGenRef bitgen) {
-  if (absl::Uniform(bitgen, 0, 256) == 'F' &&
-      absl::Uniform(bitgen, 0, 256) == 'U' &&
-      absl::Uniform(bitgen, 0, 256) == 'Z' &&
-      absl::Uniform(bitgen, 0, 256) == 'Z') {
-    std::abort();  // Bug!
+  // Driving FuzzingBitGen towards specific sequences takes a long time,
+  // so here test that the min, mean, and max values are present along with
+  // one additional value.
+  constexpr int kMax = 256;
+
+  absl::flat_hash_set<int> seen;
+  for (size_t i = 0; i < 5; i++) {
+    seen.insert(absl::Uniform(absl::IntervalClosedClosed, bitgen, 0, kMax));
+  }
+  if (seen.contains(0) && seen.contains(kMax) && seen.contains(kMax / 2) &&
+      seen.size() == 5) {
+    std::abort();
   }
 }
 FUZZ_TEST(MySuite, BitGenRef);
 
+__attribute__((optnone)) void BitGenRefShuffle(absl::BitGenRef bitgen) {
+  // This uses FuzzingBitGen's operator().
+  std::vector<int> v = {4, 1, 3, 2, 5};
+  std::shuffle(v.begin(), v.end(), bitgen);
+  if (std::is_sorted(v.begin(), v.end())) {
+    std::abort();  // Bug!
+  }
+}
+FUZZ_TEST(MySuite, BitGenRefShuffle);
+
 __attribute__((optnone)) void WithDomainClass(uint8_t a, double d) {
   // This will only crash with a=10, to make it easier to check the results.
   // d can have any value.
diff --git a/fuzztest/BUILD b/fuzztest/BUILD
index fe241cd..2cb687d 100644
--- a/fuzztest/BUILD
+++ b/fuzztest/BUILD
@@ -219,11 +219,23 @@
     ],
 )
 
+cc_test(
+    name = "fuzzing_bit_gen_test",
+    srcs = ["fuzzing_bit_gen_test.cc"],
+    deps = [
+        ":fuzzing_bit_gen",
+        "@abseil-cpp//absl/random",
+        "@abseil-cpp//absl/random:bit_gen_ref",
+        "@googletest//:gtest_main",
+    ],
+)
+
 cc_library(
     name = "fuzzing_bit_gen",
     srcs = ["fuzzing_bit_gen.cc"],
     hdrs = ["fuzzing_bit_gen.h"],
     deps = [
+        "@abseil-cpp//absl/base:core_headers",
         "@abseil-cpp//absl/base:fast_type_id",
         "@abseil-cpp//absl/base:no_destructor",
         "@abseil-cpp//absl/container:flat_hash_map",
@@ -231,7 +243,8 @@
         "@abseil-cpp//absl/numeric:int128",
         "@abseil-cpp//absl/random:bit_gen_ref",
         "@abseil-cpp//absl/types:span",
-        "@com_google_fuzztest//fuzztest/internal:register_fuzzing_mocks",
+        "@com_google_fuzztest//fuzztest/internal:fuzzing_mock_stream",
+        "@com_google_fuzztest//fuzztest/internal:register_absl_fuzzing_mocks",
     ],
 )
 
diff --git a/fuzztest/CMakeLists.txt b/fuzztest/CMakeLists.txt
index ecaf619..24f58c7 100644
--- a/fuzztest/CMakeLists.txt
+++ b/fuzztest/CMakeLists.txt
@@ -60,6 +60,7 @@
   SRCS
     "fuzzing_bit_gen.cc"
   DEPS
+    absl::core_headers
     absl::fast_type_id
     absl::no_destructor
     absl::flat_hash_map
@@ -67,7 +68,20 @@
     absl::int128
     absl::random_bit_gen_ref
     absl::span
-    fuzztest::register_fuzzing_mocks
+    fuzztest::fuzzing_mock_stream
+    fuzztest::register_absl_fuzzing_mocks
+)
+
+fuzztest_cc_test(
+  NAME
+    fuzzing_bit_gen_test
+  SRCS
+    "fuzzing_bit_gen_test.cc"
+  DEPS
+    fuzztest::fuzzing_bit_gen
+    absl::random_random
+    absl::random_bit_gen_ref
+    GTest::gmock_main
 )
 
 fuzztest_cc_library(
diff --git a/fuzztest/fuzzing_bit_gen.cc b/fuzztest/fuzzing_bit_gen.cc
index 689a8f7..0be2fb9 100644
--- a/fuzztest/fuzzing_bit_gen.cc
+++ b/fuzztest/fuzzing_bit_gen.cc
@@ -18,42 +18,115 @@
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <limits>
+#include <utility>
 
 #include "absl/base/fast_type_id.h"
 #include "absl/base/no_destructor.h"
 #include "absl/container/flat_hash_map.h"
+#include "absl/numeric/bits.h"
+#include "absl/numeric/int128.h"
 #include "absl/types/span.h"
-#include "./fuzztest/internal/register_fuzzing_mocks.h"
+#include "./fuzztest/internal/fuzzing_mock_stream.h"
+#include "./fuzztest/internal/register_absl_fuzzing_mocks.h"
 
 namespace fuzztest {
+namespace {
 
-FuzzingBitGen::FuzzingBitGen(absl::Span<const uint8_t> data_stream)
-    : data_stream_(data_stream) {
-  // Seed the internal URBG with the first 8 bytes of the data stream.
-  uint64_t stream_seed = 0x6C7FD535EDC7A62D;
-  if (!data_stream_.empty()) {
-    size_t num_bytes = std::min(sizeof(stream_seed), data_stream_.size());
-    std::memcpy(&stream_seed, data_stream_.data(), num_bytes);
-    data_stream_.remove_prefix(num_bytes);
-  }
-  seed(stream_seed);
+using FuzzingMockStream = ::fuzztest::internal::FuzzingMockStream;
+using Instruction = FuzzingMockStream::Instruction;
+
+// Minimal implementation of a PCG64 engine equivalent to xsl_rr_128_64.
+inline constexpr absl::uint128 multiplier() {
+  return absl::MakeUint128(0x2360ed051fc65da4, 0x4385df649fccf645);
+}
+inline constexpr absl::uint128 increment() {
+  return absl::MakeUint128(0x5851f42d4c957f2d, 0x14057b7ef767814f);
+}
+inline absl::uint128 lcg(absl::uint128 s) {
+  return s * multiplier() + increment();
+}
+inline uint64_t mix(absl::uint128 state) {
+  uint64_t h = absl::Uint128High64(state);
+  uint64_t rotate = h >> 58u;
+  uint64_t s = absl::Uint128Low64(state) ^ h;
+  return absl::rotr(s, rotate);
 }
 
-FuzzingBitGen::result_type FuzzingBitGen::operator()() {
-  // The non-mockable calls will consume the next 8 bytes from the data
-  // stream until it is exhausted, then they will return a value from the
-  // internal URBG.
-  if (!data_stream_.empty()) {
-    result_type x = 0;
-    size_t num_bytes = std::min(sizeof(x), data_stream_.size());
-    std::memcpy(&x, data_stream_.data(), num_bytes);
-    data_stream_.remove_prefix(num_bytes);
-    return x;
+inline uint64_t GetSeedFromDataStream(absl::Span<const uint8_t>& data_stream) {
+  // Seed the internal URBG with the first 8 bytes of the data stream.
+  uint64_t stream_seed = 0x6C7FD535EDC7A62D;
+  if (!data_stream.empty()) {
+    size_t num_bytes = std::min(sizeof(stream_seed), data_stream.size());
+    std::memcpy(&stream_seed, data_stream.data(), num_bytes);
+    data_stream.remove_prefix(num_bytes);
+  }
+  return stream_seed;
+}
+
+}  // namespace
+
+FuzzingBitGen::FuzzingBitGen(absl::Span<const uint8_t> data_stream)
+    : control_stream_({}), data_stream_(data_stream) {
+  seed(GetSeedFromDataStream(data_stream_));
+}
+
+FuzzingBitGen::FuzzingBitGen(absl::Span<const uint8_t> data_stream,
+                             absl::Span<const uint8_t> control_stream,
+                             uint64_t seed_value)
+    : control_stream_(control_stream), data_stream_(data_stream) {
+  seed(seed_value);
+}
+
+void FuzzingBitGen::DataStreamFn(bool use_lcg, void* result,
+                                 size_t result_size) {
+  if (!use_lcg && !data_stream_.empty()) {
+    // Consume up to result_size bytes from the data stream and copy to result.
+    // leaving the remaining bytes unchanged.
+    size_t n = std::min(result_size, data_stream_.size());
+    memcpy(result, data_stream_.data(), n);
+    data_stream_.remove_prefix(n);
+    return;
   }
 
-  // Fallback to the internal URBG.
-  state_ = lcg(state_);
-  return mix(state_);
+  // The stream is expired. Generate up to 16 bytes from the LCG, and copy to
+  // result, leaving the remaining bytes unchanged.
+  //
+  // NOTE: This will satisfy uniform values up to uint128, however it
+  // will not fill longer string values.
+  urbg_state_ = lcg(urbg_state_);
+  uint64_t x = mix(urbg_state_);
+  memcpy(result, &x, std::min(result_size, sizeof(x)));
+  if (result_size > sizeof(x)) {
+    urbg_state_ = lcg(urbg_state_);
+    x = mix(urbg_state_);
+    memcpy(static_cast<uint8_t*>(result) + sizeof(x), &x,
+           std::min(result_size - sizeof(x), sizeof(x)));
+  }
+}
+
+uint64_t FuzzingBitGen::operator()() {
+  // Use the control stream to determine the return value.
+  Instruction instruction =
+      FuzzingMockStream::GetNextInstruction(control_stream_);
+  switch (instruction) {
+    case Instruction::kMin:
+      return 0;
+    case Instruction::kMax:
+      return (std::numeric_limits<uint64_t>::max)();
+    case Instruction::kMean:
+      return (std::numeric_limits<uint64_t>::max)() / 2;
+    default:
+      break;
+  }
+  uint64_t x = 0;
+  DataStreamFn(instruction == Instruction::kLCGVariate, &x, sizeof(x));
+  return x;
+}
+
+void FuzzingBitGen::seed(result_type seed_value) {
+  absl::uint128 tmp = seed_value;
+  urbg_state_ = lcg(tmp + increment());
 }
 
 bool FuzzingBitGen::InvokeMock(absl::FastTypeIdType key_id, void* args_tuple,
@@ -73,7 +146,16 @@
   if (it == fuzzing_map->end()) {
     return false;
   }
-  it->second(data_stream_, args_tuple, result);
+
+  Instruction instruction =
+      FuzzingMockStream::GetNextInstruction(control_stream_);
+  bool use_lcg = instruction == Instruction::kLCGVariate;
+  it->second(FuzzingMockStream(
+                 [this, use_lcg](void* result, size_t n) {
+                   this->DataStreamFn(use_lcg, result, n);
+                 },
+                 instruction),
+             args_tuple, result);
   return true;
 }
 
diff --git a/fuzztest/fuzzing_bit_gen.h b/fuzztest/fuzzing_bit_gen.h
index f613892..780360b 100644
--- a/fuzztest/fuzzing_bit_gen.h
+++ b/fuzztest/fuzzing_bit_gen.h
@@ -15,11 +15,13 @@
 #ifndef FUZZTEST_FUZZTEST_FUZZING_BIT_GEN_H_
 #define FUZZTEST_FUZZTEST_FUZZING_BIT_GEN_H_
 
+#include <cstddef>
 #include <cstdint>
+#include <cstring>
 #include <limits>
 
+#include "absl/base/attributes.h"
 #include "absl/base/fast_type_id.h"
-#include "absl/numeric/bits.h"
 #include "absl/numeric/int128.h"
 #include "absl/random/bit_gen_ref.h"
 #include "absl/types/span.h"
@@ -27,39 +29,41 @@
 namespace fuzztest {
 
 /// FuzzingBitGen is a BitGen instance which uses the Abseil mock mechanisms
-/// to return distribution specific variates based on the fuzz data stream.
+/// to return distribution-specific variates based on the underlying control
+/// and data streams. The specific sequence generated by a FuzzingBitGen may
+/// vary due to the underlying code paths and whether implementation details
+/// change, such as adding support for new distributions, etc.
 ///
-/// It is perhaps useful to think of the data stream as a sequence of structured
-/// variates with semantic meaning, rather than just values. Recombinations of,
-/// and modifications to, the sequence are useful in exploring the behavior of
-/// the code under test in ways where a mere random-number generator sequence
-/// would not, as changing the seed mutates the entire sequence.
+/// The `control stream` is a sequence of control bytes which modifies the
+/// behavior of the mocked distribution functions, such as returning min,
+/// max, mean, or alternate values. When the control stream is exhausted,
+/// it is not reused; it behaves as though it were zero-padded.
 ///
-/// NOTE: The first 8 bytes of the fuzzed data stream may be used to seed an
-/// internal pnrg which is used to generate random variates for calls which
-/// are not captured through mockable Abseil random distribution methods
-/// (for example, calls to std::shuffle(...)). Otherwise the data stream is
-/// treated as a stream where the next value in the sequence maps to the output
-/// of the next distribution method.  Note that the specific sequence generated
-/// by a FuzzingBitGen may vary due to the underlying code paths and whether
-/// implementation details change, such as adding support for new distributions,
-/// etc.
+/// The `data_stream` is used to provide the random values for the mocked
+/// distribution functions, as well as an internal URBG used for non-mocked
+/// functions, such as std::shuffle(...).  When the data stream is exhausted,
+/// it is *not* reused (wrapped around), instead the internal URBG is used as
+/// the source of random variates.
 ///
-/// When the data stream is exhausted, absl::MockingBitGen mockable calls will
-/// continue to return an arbitrary legal value, typically the minimum or mean
-/// value of the distribution.
+/// The `seed_value` is used to seed the internal URBG.
+///
+/// NOTE: After both control and data streams are exhausted, FuzzingBitGen falls
+/// back to an internal LCG PRNG, which should avoid infinite loops in fuzzed
+/// functions.
 ///
 /// This type is thread-compatible, but not thread-safe.
 class FuzzingBitGen {
  public:
-  // Create a FuzzingBitGen from an unowned fuzzed `data` source, which must
-  // outlive the FuzzingBitGen instance.
-  //
-  // The first 8 bytes of the data stream are used to seed an internal URBG used
-  // for calls which are not mockable.
-  explicit FuzzingBitGen(absl::Span<const uint8_t> data_stream);
+  // Create a FuzzingBitGen from an unowned fuzzed `data_stream` source, an
+  // optional `control_stream` source, and an optional `seed_value`.
+  // Both streams must outlive the FuzzingBitGen.
+  explicit FuzzingBitGen(
+      absl::Span<const uint8_t> data_stream ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      absl::Span<const uint8_t> control_stream ABSL_ATTRIBUTE_LIFETIME_BOUND,
+      uint64_t seed_value);
+  explicit FuzzingBitGen(
+      absl::Span<const uint8_t> data_stream ABSL_ATTRIBUTE_LIFETIME_BOUND);
 
-  // Disallow copy, assign, and move.
   FuzzingBitGen(const FuzzingBitGen&) = delete;
   FuzzingBitGen& operator=(const FuzzingBitGen&) = delete;
   FuzzingBitGen(FuzzingBitGen&&) = default;
@@ -75,38 +79,25 @@
     return (std::numeric_limits<result_type>::max)();
   }
 
-  void seed(result_type seed_value = 0) {
-    absl::uint128 tmp = seed_value;
-    state_ = lcg(tmp + increment());
-  }
+  void seed(result_type seed_value = 0);
 
   result_type operator()();
 
  private:
-  // Minimal implementation of a PCG64 engine equivalent to xsl_rr_128_64.
-  static inline constexpr absl::uint128 multiplier() {
-    return absl::MakeUint128(0x2360ed051fc65da4, 0x4385df649fccf645);
-  }
-  static inline constexpr absl::uint128 increment() {
-    return absl::MakeUint128(0x5851f42d4c957f2d, 0x14057b7ef767814f);
-  }
-  inline absl::uint128 lcg(absl::uint128 s) {
-    return s * multiplier() + increment();
-  }
-  inline result_type mix(absl::uint128 state) {
-    uint64_t h = absl::Uint128High64(state);
-    uint64_t rotate = h >> 58u;
-    uint64_t s = absl::Uint128Low64(state) ^ h;
-    return absl::rotr(s, rotate);
-  }
+  // Consumes up to x bytes from the data stream, or if there are no remaining
+  // bytes, returns bytes from the internal LCG PRNG.
+  // `result` points to a buffer that is pre-initialized as the entire buffer
+  // may not be overwritten.
+  void DataStreamFn(bool use_lcg, void* result, size_t result_size);
 
   // InvokeMock meets the requirements of absl::BitGenRef::InvokeMock.
   // This method detects whether the key has been registered as supported,
   // and, if so, returns a value derived from `data_stream_`.
   bool InvokeMock(absl::FastTypeIdType key_id, void* args_tuple, void* result);
 
-  absl::Span<const uint8_t> data_stream_;  // Mock data stream.
-  absl::uint128 state_ = 0;                // Internal URBG state.
+  absl::uint128 urbg_state_ = 0;
+  absl::Span<const uint8_t> control_stream_;
+  absl::Span<const uint8_t> data_stream_;
 
   template <typename>
   friend struct ::absl::random_internal::DistributionCaller;  // for InvokeMock
diff --git a/fuzztest/fuzzing_bit_gen_test.cc b/fuzztest/fuzzing_bit_gen_test.cc
new file mode 100644
index 0000000..859c996
--- /dev/null
+++ b/fuzztest/fuzzing_bit_gen_test.cc
@@ -0,0 +1,102 @@
+// Copyright 2026 Google LLC
+//
+// 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
+//
+//      http://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 "./fuzztest/fuzzing_bit_gen.h"
+
+#include <cstdint>
+#include <limits>
+
+#include "gtest/gtest.h"
+#include "absl/random/bit_gen_ref.h"
+#include "absl/random/random.h"
+
+namespace fuzztest {
+namespace {
+constexpr uint8_t kDataStream[18] = {
+    1, 2,  3, 4, 5, 6, 7, 8,  //
+    9, 10,                    //
+};
+constexpr uint64_t kSeedValue = 0x0807060504030201;
+
+TEST(FuzzingBitGenTest, OperatorReturnsBytesFromStream) {
+  constexpr uint8_t kControlStream[1] = {};
+  // {} -> uses data stream, reads up to 8 bytes.
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  EXPECT_EQ(bitgen(), 0x0807060504030201);
+  EXPECT_EQ(bitgen(), 0x0A09);
+  EXPECT_NE(bitgen(), 0x0807060504030201);  // Data stream is exhausted.
+}
+
+TEST(FuzzingBitGenTest, OperatorUsesPcgForEmptyStreams) {
+  FuzzingBitGen bitgen({}, {}, kSeedValue);
+  uint64_t v1 = bitgen();
+  EXPECT_NE(v1, 0);
+  uint64_t v2 = bitgen();
+  EXPECT_NE(v2, 0);
+
+  FuzzingBitGen bitgen2({}, {}, kSeedValue);
+  EXPECT_EQ(bitgen2(), v1);
+  EXPECT_EQ(bitgen2(), v2);
+}
+
+TEST(FuzzingBitGenTest, OperatorUsesPcgForEmptyStreamsUnseeded) {
+  FuzzingBitGen bitgen({});
+  uint64_t v1 = bitgen();
+  EXPECT_NE(v1, 0);
+  uint64_t v2 = bitgen();
+  EXPECT_NE(v2, 0);
+
+  FuzzingBitGen bitgen2({});
+  EXPECT_EQ(bitgen2(), v1);
+  EXPECT_EQ(bitgen2(), v2);
+}
+
+TEST(FuzzingBitGenTest, OperatorUsesControlStream) {
+  constexpr uint8_t kControlStream[7] = {
+      0,  // data stream variate
+      1,  // lcg variate
+      2,  // min
+      3,  // max
+      4,  // mean
+      5,  // alternate variate
+      0,  // data stream variate
+  };
+  FuzzingBitGen bitgen(kDataStream, kControlStream, 0);
+  EXPECT_EQ(bitgen(), 0x0807060504030201);
+  EXPECT_NE(bitgen(), 0);
+  EXPECT_EQ(bitgen(), 0);                                         // min
+  EXPECT_EQ(bitgen(), std::numeric_limits<uint64_t>::max());      // max
+  EXPECT_EQ(bitgen(), std::numeric_limits<uint64_t>::max() / 2);  // mean
+  EXPECT_EQ(bitgen(), 0x0a09);                                    // alternate
+  EXPECT_EQ(bitgen(), 0);
+}
+
+TEST(FuzzingBitGenTest, MockingIsRepeatable) {
+  FuzzingBitGen bg1(kDataStream, {}, kSeedValue);
+  absl::BitGenRef ref1(bg1);
+  int v_a = absl::Uniform<int>(ref1, 0, 100);
+  int v_b = absl::Uniform<int>(ref1, 0, 100);
+  int v_c = absl::Uniform<int>(ref1, 0, 100);
+  uint64_t v_d = ref1();
+
+  FuzzingBitGen bg2(kDataStream, {}, kSeedValue);
+  absl::BitGenRef ref2(bg2);
+  EXPECT_EQ(absl::Uniform<int>(ref2, 0, 100), v_a);
+  EXPECT_EQ(absl::Uniform<int>(ref2, 0, 100), v_b);
+  EXPECT_EQ(absl::Uniform<int>(ref2, 0, 100), v_c);
+  EXPECT_EQ(ref2(), v_d);
+}
+
+}  // namespace
+}  // namespace fuzztest
diff --git a/fuzztest/internal/BUILD b/fuzztest/internal/BUILD
index c5d988a..2a9c00a 100644
--- a/fuzztest/internal/BUILD
+++ b/fuzztest/internal/BUILD
@@ -307,15 +307,35 @@
 )
 
 cc_library(
-    name = "register_fuzzing_mocks",
-    srcs = ["register_fuzzing_mocks.cc"],
-    hdrs = ["register_fuzzing_mocks.h"],
+    name = "fuzzing_mock_stream",
+    hdrs = ["fuzzing_mock_stream.h"],
     deps = [
+        "@abseil-cpp//absl/functional:function_ref",
+        "@abseil-cpp//absl/numeric:bits",
+        "@abseil-cpp//absl/types:span",
+    ],
+)
+
+cc_library(
+    name = "register_absl_fuzzing_mocks",
+    srcs = ["register_absl_fuzzing_mocks.cc"],
+    hdrs = ["register_absl_fuzzing_mocks.h"],
+    deps = [
+        ":fuzzing_mock_stream",
         "@abseil-cpp//absl/base:fast_type_id",
         "@abseil-cpp//absl/functional:function_ref",
         "@abseil-cpp//absl/random:distributions",
-        "@abseil-cpp//absl/types:span",
-        "@com_google_fuzztest//common:logging",
+    ],
+)
+
+cc_test(
+    name = "register_absl_fuzzing_mocks_test",
+    srcs = ["register_absl_fuzzing_mocks_test.cc"],
+    deps = [
+        "@abseil-cpp//absl/random",
+        "@abseil-cpp//absl/random:bit_gen_ref",
+        "@com_google_fuzztest//fuzztest:fuzzing_bit_gen",
+        "@googletest//:gtest_main",
     ],
 )
 
diff --git a/fuzztest/internal/CMakeLists.txt b/fuzztest/internal/CMakeLists.txt
index 1a674bc..4ce2d47 100644
--- a/fuzztest/internal/CMakeLists.txt
+++ b/fuzztest/internal/CMakeLists.txt
@@ -273,12 +273,24 @@
 
 fuzztest_cc_library(
   NAME
-    register_fuzzing_mocks
+    fuzzing_mock_stream
   HDRS
-    "register_fuzzing_mocks.h"
-  SRCS
-    "register_fuzzing_mocks.cc"
+    "fuzzing_mock_stream.h"
   DEPS
+    absl::function_ref
+    absl::bits
+    absl::span
+)
+
+fuzztest_cc_library(
+  NAME
+    register_absl_fuzzing_mocks
+  HDRS
+    "register_absl_fuzzing_mocks.h"
+  SRCS
+    "register_absl_fuzzing_mocks.cc"
+  DEPS
+    fuzztest::fuzzing_mock_stream
     absl::fast_type_id
     absl::function_ref
     absl::random_distributions
@@ -286,6 +298,19 @@
     fuzztest::common_logging
 )
 
+fuzztest_cc_test(
+  NAME
+    register_absl_fuzzing_mocks_test
+  SRCS
+    "register_absl_fuzzing_mocks_test.cc"
+  DEPS
+    fuzztest::register_absl_fuzzing_mocks
+    fuzztest::fuzzing_bit_gen
+    absl::random_random
+    absl::random_bit_gen_ref
+    GTest::gmock_main
+)
+
 fuzztest_cc_library(
   NAME
     registration
diff --git a/fuzztest/internal/domains/arbitrary_impl.h b/fuzztest/internal/domains/arbitrary_impl.h
index f6b1702..518b8b2 100644
--- a/fuzztest/internal/domains/arbitrary_impl.h
+++ b/fuzztest/internal/domains/arbitrary_impl.h
@@ -449,8 +449,8 @@
 // improve if possible.
 template <typename T, int N = *DetectBindableFieldCount<T>()>
 decltype(DetectAggregateOfImpl2<T>(
-    BindAggregate(std::declval<T&>(), std::integral_constant<int, N>{})))
-DetectAggregateOfImpl();
+    BindAggregate(std::declval<T&>(),
+                  std::integral_constant<int, N>{}))) DetectAggregateOfImpl();
 
 template <typename T>
 class ArbitraryImpl<
@@ -584,13 +584,42 @@
 // Arbitrary for absl::BitGenRef.
 template <>
 class ArbitraryImpl<absl::BitGenRef>
-    : public BitGenRefDomain<SequenceContainerOfImpl<std::vector<uint8_t>,
-                                                     ArbitraryImpl<uint8_t>>> {
-  using InnerContainer =
+    : public BitGenRefDomain<
+          /*Tuple=*/
+          AggregateOfImpl<
+              std::tuple<std::vector<uint8_t>, std::vector<uint8_t>, uint64_t>,
+              internal::RequireCustomCorpusType::kNo,
+              /*DataSequence=*/
+              SequenceContainerOfImpl<std::vector<uint8_t>,
+                                      ArbitraryImpl<uint8_t>>,
+              /*ControlSequence=*/
+              SequenceContainerOfImpl<std::vector<uint8_t>,
+                                      ElementOfImpl<uint8_t>>,
+              /*Seed=*/ElementOfImpl<uint64_t> /**/
+              >> {
+  using ValueType =
+      std::tuple<std::vector<uint8_t>, std::vector<uint8_t>, uint64_t>;
+  // The control stream determines the behavior of the FuzzingBitGen;
+  // See the implementation for valid value, which may change.
+
+  using DataSequence =
       SequenceContainerOfImpl<std::vector<uint8_t>, ArbitraryImpl<uint8_t>>;
+  using ControlSequence =
+      SequenceContainerOfImpl<std::vector<uint8_t>, ElementOfImpl<uint8_t>>;
+
+  // NOTE: The seed could be an ArbitraryImpl<uint64_t>.
+  using Seed = ElementOfImpl<uint64_t>;
+
+  using Tuple =
+      AggregateOfImpl<ValueType, internal::RequireCustomCorpusType::kNo,
+                      DataSequence, ControlSequence, Seed>;
 
  public:
-  ArbitraryImpl() : BitGenRefDomain(InnerContainer{}.WithMinSize(8)) {}
+  ArbitraryImpl()
+      : BitGenRefDomain(
+            Tuple(std::in_place, DataSequence{},
+                  ControlSequence{ElementOfImpl<uint8_t>({0, 1, 2, 3, 4, 5})},
+                  Seed({0}))) {}
 };
 
 }  // namespace fuzztest::internal
diff --git a/fuzztest/internal/domains/bit_gen_ref.h b/fuzztest/internal/domains/bit_gen_ref.h
index 824d35e..6b882bc 100644
--- a/fuzztest/internal/domains/bit_gen_ref.h
+++ b/fuzztest/internal/domains/bit_gen_ref.h
@@ -17,6 +17,7 @@
 
 #include <cstdint>
 #include <optional>
+#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -26,6 +27,7 @@
 #include "./common/logging.h"
 #include "./fuzztest/fuzzing_bit_gen.h"
 #include "./fuzztest/internal/domains/domain_base.h"
+#include "./fuzztest/internal/meta.h"
 #include "./fuzztest/internal/printer.h"
 #include "./fuzztest/internal/serialization.h"
 
@@ -37,56 +39,62 @@
 //
 // The URBG instance is lazily initialized when GetBitGen is called and
 // destroyed when CleanupBitGen is called.
-class BitGenCorpusValue {
+template <typename InnerCorpus>
+class BitGenCorpus {
  public:
-  using InitializerData = std::vector<uint8_t>;
   using URBG = FuzzingBitGen;
 
-  explicit BitGenCorpusValue(InitializerData data)
-      : initializer_data_(std::move(data)) {}
-  ~BitGenCorpusValue() { CleanupBitGen(); }
+  explicit BitGenCorpus(InnerCorpus inner_corpus)
+      : inner_corpus_(std::move(inner_corpus)) {}
+  ~BitGenCorpus() { CleanupBitGen(); }
 
   // Copy and move do not initialize the internal URBG instance.
-  BitGenCorpusValue(const BitGenCorpusValue& o)
-      : initializer_data_(o.initializer_data_), bitgen_(std::nullopt) {}
-  BitGenCorpusValue& operator=(const BitGenCorpusValue& o) {
+  BitGenCorpus(const BitGenCorpus& o)
+      : inner_corpus_(o.inner_corpus_), bitgen_(std::nullopt) {}
+  BitGenCorpus& operator=(const BitGenCorpus& o) {
     // The internal URBG should be unused.
     FUZZTEST_CHECK(!bitgen_.has_value());
-    initializer_data_ = o.initializer_data_;
+    inner_corpus_ = o.inner_corpus_;
     return *this;
   }
-  BitGenCorpusValue(BitGenCorpusValue&& o)
-      : initializer_data_(std::move(o.initializer_data_)),
-        bitgen_(std::nullopt) {}
-  BitGenCorpusValue& operator=(BitGenCorpusValue&& o) {
+  BitGenCorpus(BitGenCorpus&& o)
+      : inner_corpus_(std::move(o.inner_corpus_)), bitgen_(std::nullopt) {}
+  BitGenCorpus& operator=(BitGenCorpus&& o) {
     // The internal URBG should be unused.
     FUZZTEST_CHECK(!o.bitgen_.has_value());
     FUZZTEST_CHECK(!bitgen_.has_value());
-    initializer_data_ = std::move(o.initializer_data_);
+    inner_corpus_ = std::move(o.inner_corpus_);
     return *this;
   }
 
-  InitializerData& initializer_data() { return initializer_data_; }
-  const InitializerData& initializer_data() const { return initializer_data_; }
+  InnerCorpus& inner_corpus() { return inner_corpus_; }
+  const InnerCorpus& inner_corpus() const { return inner_corpus_; }
 
   // Cleanup the internal URBG instance.
   void CleanupBitGen() { bitgen_.reset(); }
 
   // Returns a reference to the URBG instance.
   // If it has not been initialized, it will be initialized.
-  // NOTE: The returned reference is valid until the next call to CleanupBitGen.
-  URBG& GetBitGen() const {
+  // The returned reference is valid until the next call to CleanupBitGen.
+  template <typename InnerDomain>
+  URBG& GetBitGen(InnerDomain& domain) const {
+    static_assert(std::is_same_v<InnerCorpus, corpus_type_t<InnerDomain>>);
     if (!bitgen_.has_value()) {
-      bitgen_.emplace(initializer_data_);
+      uint64_t seed = 0;
+      std::tie(data_stream_, control_stream_, seed) =
+          domain.GetValue(inner_corpus_);
+      // The values are copied so that they can outlive the URBG instance.
+      bitgen_.emplace(data_stream_, control_stream_, seed);
     }
     return *bitgen_;
   }
 
  private:
-  // Underlying fuzzed data stream; the input to the URBG constructor.
-  // When using util_random::FuzzingBitGen, this is a vector of uint8_t which
-  // defines the sequence of random variates.
-  std::vector<uint8_t> initializer_data_;
+  InnerCorpus inner_corpus_;
+
+  // Inputs to the FuzzingBitGen constructor which must outlive it.
+  mutable std::vector<uint8_t> data_stream_;
+  mutable std::vector<uint8_t> control_stream_;
   mutable std::optional<URBG> bitgen_;
 };
 
@@ -99,21 +107,24 @@
 //
 // The domain accepts an input "data stream" corpus which is used to initialize
 // a FuzzingBitGen instance. This internal FuzzingBitGen instance is bound to an
-// absl::BitGenRef when GetValue is called.
+// absl::BitGenRef when GetValue is called. The control stream is reused
+// (wrapped around) when exhausted. The data stream falls back to an LCG PRNG
+// when exhausted.
 //
 // BitGenRefDomain does not support seeded domains.
 // BitGenRefDomain does not support GetRandomValue.
-template <typename Inner>
-class BitGenRefDomain
-    : public domain_implementor::DomainBase<BitGenRefDomain<Inner>,
-                                            /*value_type=*/absl::BitGenRef,
-                                            /*corpus_type=*/BitGenCorpusValue> {
+template <typename InnerDomain>
+class BitGenRefDomain : public domain_implementor::DomainBase<
+                            /*Derived=*/BitGenRefDomain<InnerDomain>,
+                            /*value_type=*/absl::BitGenRef,
+                            /*corpus_type=*/
+                            BitGenCorpus<corpus_type_t<InnerDomain>>> {
  public:
   using typename BitGenRefDomain::DomainBase::corpus_type;
   using typename BitGenRefDomain::DomainBase::value_type;
 
-  explicit BitGenRefDomain(const Inner& inner) : inner_(inner) {}
-  explicit BitGenRefDomain(Inner&& inner) : inner_(std::move(inner)) {}
+  explicit BitGenRefDomain(const InnerDomain& inner) : inner_(inner) {}
+  explicit BitGenRefDomain(InnerDomain&& inner) : inner_(std::move(inner)) {}
 
   BitGenRefDomain(const BitGenRefDomain&) = default;
   BitGenRefDomain(BitGenRefDomain&&) = default;
@@ -121,17 +132,17 @@
   BitGenRefDomain& operator=(BitGenRefDomain&&) = default;
 
   corpus_type Init(absl::BitGenRef prng) {
-    return corpus_type{inner_.Init(prng)};
+    return corpus_type(inner_.Init(prng));
   }
   void Mutate(corpus_type& corpus_value, absl::BitGenRef prng,
               const domain_implementor::MutationMetadata& metadata,
               bool only_shrink) {
     corpus_value.CleanupBitGen();
-    inner_.Mutate(corpus_value.initializer_data(), prng, metadata, only_shrink);
+    inner_.Mutate(corpus_value.inner_corpus(), prng, metadata, only_shrink);
   }
 
-  absl::BitGenRef GetValue(const corpus_type& corpus_value) const {
-    return absl::BitGenRef(corpus_value.GetBitGen());
+  value_type GetValue(const corpus_type& corpus_value) const {
+    return corpus_value.GetBitGen(inner_);
   }
 
   value_type GetRandomValue(absl::BitGenRef prng) {
@@ -143,24 +154,28 @@
     // No conversion from absl::BitGenRef back to corpus.
     return std::nullopt;
   }
+
   absl::Status ValidateCorpusValue(const corpus_type& corpus_value) const {
-    return inner_.ValidateCorpusValue(corpus_value.initializer_data());
+    return inner_.ValidateCorpusValue(corpus_value.inner_corpus());
   }
+
   void UpdateMemoryDictionary(
       const corpus_type& corpus_value,
       domain_implementor::ConstCmpTablesPtr cmp_tables) {
-    return inner_.UpdateMemoryDictionary(corpus_value.initializer_data(),
+    return inner_.UpdateMemoryDictionary(corpus_value.inner_corpus(),
                                          cmp_tables);
   }
+
   std::optional<corpus_type> ParseCorpus(const internal::IRObject& obj) const {
     auto x = inner_.ParseCorpus(obj);
-    if (x.has_value()) {
-      return corpus_type(*std::move(x));
+    if (!x.has_value()) {
+      return std::nullopt;
     }
-    return std::nullopt;
+    return corpus_type(*std::move(x));
   }
+
   internal::IRObject SerializeCorpus(const corpus_type& corpus_value) const {
-    return inner_.SerializeCorpus(corpus_value.initializer_data());
+    return inner_.SerializeCorpus(corpus_value.inner_corpus());
   }
 
   auto GetPrinter() const { return Printer{}; }
@@ -170,11 +185,29 @@
     void PrintCorpusValue(const corpus_type& val,
                           domain_implementor::RawSink out,
                           domain_implementor::PrintMode mode) const {
-      absl::Format(out, "absl::BitGenRef{}");
+      absl::Format(out, "FuzzingBitGen({");
+      bool first = true;
+      for (const auto& x : std::get<0>(val.inner_corpus())) {
+        if (!first) {
+          absl::Format(out, ", ");
+        }
+        absl::Format(out, "%d", x);
+        first = false;
+      }
+      absl::Format(out, "}, {");
+      first = true;
+      for (const auto& x : std::get<1>(val.inner_corpus())) {
+        if (!first) {
+          absl::Format(out, ", ");
+        }
+        absl::Format(out, "%d", x);
+        first = false;
+      }
+      absl::Format(out, "}, 0x%016x)", std::get<2>(val.inner_corpus()));
     }
   };
 
-  Inner inner_;
+  InnerDomain inner_;
 };
 
 }  // namespace fuzztest::internal
diff --git a/fuzztest/internal/domains/container_of_impl.h b/fuzztest/internal/domains/container_of_impl.h
index 0bb9d79..7a8cd45 100644
--- a/fuzztest/internal/domains/container_of_impl.h
+++ b/fuzztest/internal/domains/container_of_impl.h
@@ -99,7 +99,7 @@
       is_vector_or_string;
 
   // If `!container_has_memory_dict`, dict_type is a bool and dict
-  // is not used. This conditional_t may be neccessary because some
+  // is not used. This conditional_t may be necessary because some
   // value_type may not have copy constructors(for example, proto).
   // Making it a safe type(bool) to not break some targets.
   using dict_type = std::conditional_t<container_has_memory_dict,
diff --git a/fuzztest/internal/fuzzing_mock_stream.h b/fuzztest/internal/fuzzing_mock_stream.h
new file mode 100644
index 0000000..ca5430a
--- /dev/null
+++ b/fuzztest/internal/fuzzing_mock_stream.h
@@ -0,0 +1,127 @@
+// Copyright 2025 Google LLC
+//
+// 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
+//
+//      http://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.
+
+#ifndef FUZZTEST_FUZZTEST_INTERNAL_FUZZING_MOCK_STREAM_H_
+#define FUZZTEST_FUZZTEST_INTERNAL_FUZZING_MOCK_STREAM_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+#include "absl/functional/function_ref.h"
+#include "absl/numeric/bits.h"
+#include "absl/types/span.h"
+
+namespace fuzztest::internal {
+
+class FuzzingMockStream {
+ public:
+  // DataStreamFn copies up to n bytes from the data stream to the buffer
+  // pointer.
+  using DataStreamFn = absl::FunctionRef<void(void*, size_t)>;
+
+  // Instruction is used to select an implementation-specific random number
+  // variate when invoking the fuzztest mock.
+  enum class Instruction : uint8_t {
+    // Use the data stream to generate the random number.
+    kDataStreamVariate = 0,
+    // Use the LCG algorithm to generate the random number.
+    kLCGVariate = 1,
+    kMin = 2,   // Return the minimum value of the distribution.
+    kMax = 3,   // Return the maximum value of the distribution.
+    kMean = 4,  // Return the mean of the distribution.
+
+    // Return an alternate value of the distribution. This is typically an edge
+    // case value that is unlikely to be returned by the normal distribution.
+    kAlternateVariate = 5,
+  };
+
+  FuzzingMockStream(DataStreamFn data_stream_fn, Instruction instruction)
+      : data_stream_fn_(data_stream_fn), instruction_(instruction) {}
+
+  static Instruction GetNextInstruction(
+      absl::Span<const uint8_t>& control_stream) {
+    if (control_stream.empty()) {
+      return Instruction::kDataStreamVariate;
+    }
+    uint8_t v = control_stream[0];
+    control_stream.remove_prefix(1);
+    return static_cast<Instruction>(v % 6);
+  }
+
+  Instruction instruction() { return instruction_; }
+
+  void get_bytes(void* result, size_t result_size) {
+    data_stream_fn_(result, result_size);
+  }
+
+  template <typename T>
+  T get_int_value() {
+    T x = 0;
+    data_stream_fn_(&x, sizeof(x));
+    return x;
+  }
+
+  // Consumes bytes from the data stream to generate a random integer in the
+  // range [0, range].
+  template <typename T>
+  T get_int_value_in_range(uint64_t range) {
+    if (range == 0) {
+      return 0;
+    }
+    uint64_t x = 0;
+    if (range <= (std::numeric_limits<uint8_t>::max)()) {
+      x = get_int_value<uint8_t>();
+    } else if (range <= (std::numeric_limits<uint16_t>::max)()) {
+      x = get_int_value<uint16_t>();
+    } else if (range <= (std::numeric_limits<uint32_t>::max)()) {
+      x = get_int_value<uint32_t>();
+    } else {
+      x = get_int_value<uint64_t>();
+    }
+    if (range == std::numeric_limits<uint64_t>::max() ||
+        absl::has_single_bit(range + 1)) {
+      return static_cast<T>(x & range);  // range is a mask of 2^N-1
+    } else {
+      return static_cast<T>(x % (range + 1));
+    }
+  }
+
+  // URBG interface.
+  using result_type = uint64_t;
+
+  static constexpr result_type(min)() {
+    return (std::numeric_limits<result_type>::min)();
+  }
+  static constexpr result_type(max)() {
+    return (std::numeric_limits<result_type>::max)();
+  }
+
+  void reset() {}
+
+  uint64_t operator()() { return get_int_value<uint64_t>(); }
+
+ private:
+  DataStreamFn data_stream_fn_;
+  Instruction instruction_;
+};
+
+// TypeErasedFuzzFunctionT(fuzzing_mock_stream, args_tuple, result)
+// is a type erased function pointer for use with absl::MockingBitGen and
+// fuzztest mocking.
+using TypeErasedFuzzFunctionT = void (*)(FuzzingMockStream, void*, void*);
+
+}  // namespace fuzztest::internal
+
+#endif  // FUZZTEST_FUZZTEST_INTERNAL_FUZZING_MOCK_STREAM_H_
diff --git a/fuzztest/internal/register_absl_fuzzing_mocks.cc b/fuzztest/internal/register_absl_fuzzing_mocks.cc
new file mode 100644
index 0000000..3612fb9
--- /dev/null
+++ b/fuzztest/internal/register_absl_fuzzing_mocks.cc
@@ -0,0 +1,443 @@
+// Copyright 2025 Google LLC
+//
+// 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
+//
+//      http://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 "./fuzztest/internal/register_absl_fuzzing_mocks.h"
+
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/fast_type_id.h"
+#include "absl/functional/function_ref.h"
+#include "absl/random/bernoulli_distribution.h"
+#include "absl/random/beta_distribution.h"
+#include "absl/random/distributions.h"
+#include "absl/random/exponential_distribution.h"
+#include "absl/random/gaussian_distribution.h"
+#include "absl/random/log_uniform_int_distribution.h"
+#include "absl/random/poisson_distribution.h"
+#include "absl/random/zipf_distribution.h"
+#include "./fuzztest/internal/fuzzing_mock_stream.h"
+
+namespace fuzztest::internal {
+namespace {
+
+using Instruction = FuzzingMockStream::Instruction;
+
+// Bernoulli
+struct ImplBernoulli {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::bernoulli_distribution;
+  using ArgTupleT = std::tuple<double>;
+  using ResultT = bool;
+
+  ResultT operator()(double p) {
+    // Just generate a boolean; mostly ignoring p.
+    // The 0/1 cases are special cased to avoid returning false on constants.
+    if (p <= 0.0) {
+      return false;
+    } else if (p >= 1.0) {
+      return true;
+    }
+    switch (urbg.instruction()) {
+      case Instruction::kMin:
+        return false;
+      case Instruction::kMax:
+        return true;
+      case Instruction::kMean:
+        return p >= 0.5;
+      default:
+        break;
+    }
+    return urbg.get_int_value<uint8_t>() & 1;
+  }
+};
+
+// Beta
+template <typename RealType>
+struct ImplBeta {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::beta_distribution<RealType>;
+  using ArgTupleT = std::tuple<RealType, RealType>;
+  using ResultT = RealType;
+
+  ResultT operator()(RealType a, RealType b) {
+    switch (urbg.instruction()) {
+      case Instruction::kMin:
+        return 0.0;
+      case Instruction::kMax:
+        return 1.0;
+      case Instruction::kMean:
+        return a / (a + b);  // mean
+      default:
+        break;
+    }
+    return DistrT(a, b)(urbg);
+  }
+};
+
+// Exponential
+template <typename RealType>
+struct ImplExponential {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::exponential_distribution<RealType>;
+  using ArgTupleT = std::tuple<RealType>;
+  using ResultT = RealType;
+
+  ResultT operator()(RealType lambda) {
+    switch (urbg.instruction()) {
+      case Instruction::kMin:
+        return 0;
+      case Instruction::kMean:
+        return RealType{1} / lambda;  // mean
+      case Instruction::kMax:
+        return (std::numeric_limits<RealType>::max)();
+      case Instruction::kAlternateVariate:
+        return absl::uniform_real_distribution<RealType>(
+            0, (std::numeric_limits<RealType>::max)())(urbg);
+      default:
+        break;
+    }
+    return DistrT(lambda)(urbg);
+  }
+};
+
+// Gaussian
+template <typename RealType>
+struct ImplGaussian {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::gaussian_distribution<RealType>;
+  using ArgTupleT = std::tuple<RealType, RealType>;
+  using ResultT = RealType;
+
+  ResultT operator()(RealType mean, RealType sigma) {
+    const auto ten_sigma = sigma * 10;
+    switch (urbg.instruction()) {
+      // Technically the min/max are -inf/+inf.
+      case Instruction::kMin:
+        return -(std::numeric_limits<RealType>::max)();
+      case Instruction::kMax:
+        return (std::numeric_limits<RealType>::max)();
+      case Instruction::kMean:
+        return mean;
+      case Instruction::kAlternateVariate:
+        // this makes unlikely values much more likely.
+        return absl::uniform_real_distribution<RealType>(
+            mean - ten_sigma, mean + ten_sigma)(urbg);
+      default:
+        break;
+    }
+    return DistrT(mean, sigma)(urbg);
+  }
+};
+
+// LogUniform
+template <typename IntType>
+struct ImplLogUniform {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::log_uniform_int_distribution<IntType>;
+  using ArgTupleT = std::tuple<IntType, IntType, IntType>;
+  using ResultT = IntType;
+
+  ResultT operator()(IntType a, IntType b, IntType base) {
+    switch (urbg.instruction()) {
+      case Instruction::kMin:
+        return a;
+      case Instruction::kMax:
+        return b;
+      case Instruction::kMean:
+        if (a > 0 && b > a) {
+          double log_b_over_a = std::log(static_cast<double>(b) / a);
+          return static_cast<IntType>(static_cast<double>(b - a) /
+                                      log_b_over_a);
+        }
+        break;
+      case Instruction::kAlternateVariate:
+        return urbg.get_int_value_in_range<IntType>(b - a) + a;
+      default:
+        break;
+    }
+    return DistrT(a, b, base)(urbg);
+  }
+};
+
+// Poisson
+template <typename IntType>
+struct ImplPoisson {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::poisson_distribution<IntType>;
+  using ArgTupleT = std::tuple<double>;
+  using ResultT = IntType;
+
+  ResultT operator()(double lambda) {
+    switch (urbg.instruction()) {
+      case Instruction::kMin:
+        return 0;
+      case Instruction::kMax:
+        return (std::numeric_limits<IntType>::max)();
+      case Instruction::kMean:
+        return static_cast<IntType>(lambda);
+      case Instruction::kAlternateVariate:
+        return urbg.get_int_value_in_range<IntType>(
+            (std::numeric_limits<IntType>::max)());
+      default:
+        break;
+    }
+    return DistrT(lambda)(urbg);
+  }
+};
+
+// Zipf
+template <typename IntType>
+struct ImplZipf {
+  FuzzingMockStream urbg;
+
+  using DistrT = absl::zipf_distribution<IntType>;
+  using ArgTupleT = std::tuple<IntType, double, double>;
+  using ResultT = IntType;
+
+  ResultT operator()(IntType k, double q, double v) {
+    switch (urbg.instruction()) {
+      case Instruction::kMin:
+        return 0;
+      case Instruction::kMax:
+        return k;
+      case Instruction::kAlternateVariate:
+        return urbg.get_int_value_in_range<IntType>(k);
+      default:
+        break;
+    }
+    return DistrT(k, q, v)(urbg);
+  }
+};
+
+// Uniform
+template <typename R>
+struct ImplUniform {
+  FuzzingMockStream urbg;
+  using DistrT = absl::random_internal::UniformDistributionWrapper<R>;
+  using ResultT = R;
+
+  ResultT operator()(absl::IntervalClosedClosedTag, R min, R max) {
+    if constexpr (std::is_floating_point_v<R>) {
+      return operator()(absl::IntervalClosedOpen, min,
+                        std::nexttoward(max, (std::numeric_limits<R>::max)()));
+    }
+    // Only int-typed calls should reach here.
+    if constexpr (std::is_integral_v<R>) {
+      switch (urbg.instruction()) {
+        case Instruction::kMin:
+          return min;
+        case Instruction::kMax:
+          return max;
+        case Instruction::kMean:
+          return min + ((max - min) / 2);
+        case Instruction::kAlternateVariate:
+          // This is non-uniform, but it consumes fewer bytes from data stream.
+          if constexpr (sizeof(R) <= sizeof(uint64_t)) {
+            return min +
+                   urbg.get_int_value_in_range<R>(static_cast<uint64_t>(max) -
+                                                  static_cast<uint64_t>(min));
+          }
+        default:
+          break;
+      }
+      // Fallback to absl::uniform_int_distribution.
+      return absl::uniform_int_distribution<R>(min, max)(urbg);
+    } else {
+      return 0;
+    }
+  }
+
+  ResultT operator()(absl::IntervalClosedOpenTag, R min, R max) {
+    if constexpr (std::is_integral_v<R>) {
+      return operator()(absl::IntervalClosedClosed, min, max - 1);
+    }
+    // Only real-typed calls should reach here.
+    if constexpr (std::is_floating_point_v<R>) {
+      switch (urbg.instruction()) {
+        case Instruction::kMin:
+          return min;
+        case Instruction::kMax:
+          return std::nexttoward(max, std::numeric_limits<R>::min());
+        case Instruction::kMean:
+          return min + ((max - min) / 2);
+        default:
+          break;
+      }
+      return absl::uniform_real_distribution<R>(min, max)(urbg);
+    } else {
+      return 0;
+    }
+  }
+
+  ResultT operator()(absl::IntervalOpenOpenTag, R min, R max) {
+    if constexpr (std::is_floating_point_v<R>) {
+      return operator()(absl::IntervalClosedOpen, std::nexttoward(min, max),
+                        max);
+    } else {
+      return operator()(absl::IntervalClosedOpen, min + 1, max);
+    }
+  }
+
+  ResultT operator()(absl::IntervalOpenClosedTag, R min, R max) {
+    if constexpr (std::is_floating_point_v<R>) {
+      return operator()(absl::IntervalClosedClosed, std::nexttoward(min, max),
+                        max);
+    } else {
+      return operator()(absl::IntervalClosedClosed, min + 1, max);
+    }
+  }
+
+  ResultT operator()(R min, R max) {
+    return operator()(absl::IntervalClosedOpen, min, max);
+  }
+
+  ResultT operator()() {
+    static_assert(std::is_unsigned_v<R>);
+    return operator()(absl::IntervalClosedClosed, 0,
+                      (std::numeric_limits<R>::max)());
+  }
+};
+
+// -----------------------------------------------------------------------------
+
+// InvokeFuzzFunction is a type-erased function pointer which is responsible
+// for casting the args_tuple and result parameters to the correct types and
+// then invoking the implementation functor. It is important that the
+// ArgsTupleT and ResultT types match the types of the distribution and the
+// implementation functions, so the HandleFuzzedFunction overloads are used to
+// determine the correct types.
+template <typename FuzzFunctionT, typename ResultT, typename ArgTupleT>
+void InvokeFuzzFunction(FuzzingMockStream stream, void* args_tuple,
+                        void* result) {
+  FuzzFunctionT fn{std::move(stream)};
+  *static_cast<ResultT*>(result) =
+      absl::apply(fn, *static_cast<ArgTupleT*>(args_tuple));
+}
+
+template <typename FuzzFunctionT>
+void HandleFuzzedFunctionX(
+    absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
+        register_fn) {
+  using DistrT = typename FuzzFunctionT::DistrT;
+  using ArgTupleT = typename FuzzFunctionT::ArgTupleT;
+  using ResultT = typename FuzzFunctionT::ResultT;
+  using KeyT = ResultT(DistrT, ArgTupleT);
+
+  register_fn(absl::FastTypeId<KeyT>(),
+              &InvokeFuzzFunction<FuzzFunctionT, ResultT, ArgTupleT>);
+}
+
+template <typename FuzzFunctionT, typename... Args>
+void HandleFuzzedFunctionU(
+    absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
+        register_fn) {
+  using DistrT = typename FuzzFunctionT::DistrT;
+  using ArgTupleT = std::tuple<std::decay_t<Args>...>;
+  using ResultT = typename FuzzFunctionT::ResultT;
+  using KeyT = ResultT(DistrT, ArgTupleT);
+
+  register_fn(absl::FastTypeId<KeyT>(),
+              &InvokeFuzzFunction<FuzzFunctionT, ResultT, ArgTupleT>);
+}
+
+// -----------------------------------------------------------------------------
+// X_ macros to invoke X_IMPL_T macros for each type.
+// -----------------------------------------------------------------------------
+
+#define X_SINT(Impl)                                  \
+  if constexpr (std::is_signed_v<char> &&             \
+                !std::is_same_v<char, signed char>) { \
+    X_IMPL_T(char, Impl);                             \
+  }                                                   \
+  X_IMPL_T(signed char, Impl);                        \
+  X_IMPL_T(short, Impl);     /*NOLINT*/               \
+  X_IMPL_T(long, Impl);      /*NOLINT*/               \
+  X_IMPL_T(long long, Impl); /*NOLINT*/               \
+  X_IMPL_T(int, Impl)
+
+#define X_UINT(Impl)                                    \
+  if constexpr (std::is_unsigned_v<char> &&             \
+                !std::is_same_v<char, unsigned char>) { \
+    X_IMPL_T(char, Impl);                               \
+  }                                                     \
+  X_IMPL_T(unsigned char, Impl);                        \
+  X_IMPL_T(unsigned short, Impl);     /*NOLINT*/        \
+  X_IMPL_T(unsigned long, Impl);      /*NOLINT*/        \
+  X_IMPL_T(unsigned long long, Impl); /*NOLINT*/        \
+  X_IMPL_T(unsigned int, Impl)
+
+#define X_REAL(Impl)     \
+  X_IMPL_T(float, Impl); \
+  X_IMPL_T(double, Impl)
+
+#define X_XINT(Impl) \
+  X_SINT(Impl);      \
+  X_UINT(Impl)
+
+#define X_ALL(Impl) \
+  X_SINT(Impl);     \
+  X_UINT(Impl);     \
+  X_REAL(Impl)
+
+}  // namespace
+
+// Registers the fuzzing functions into the fuzztest mock map.
+void RegisterAbslRandomFuzzingMocks(
+    absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
+        register_fn) {
+#define X_IMPL_T(T, Impl) HandleFuzzedFunctionX<Impl<T>>(register_fn)
+
+  HandleFuzzedFunctionX<ImplBernoulli>(register_fn);
+
+  X_REAL(ImplBeta);
+  X_REAL(ImplExponential);
+  X_REAL(ImplGaussian);
+  X_XINT(ImplLogUniform);
+  X_XINT(ImplPoisson);
+  X_XINT(ImplZipf);
+
+#undef X_IMPL_T
+#define X_IMPL_T(T, Impl)                                              \
+  HandleFuzzedFunctionU<Impl<T>, absl::IntervalOpenOpenTag, T, T>(     \
+      register_fn);                                                    \
+  HandleFuzzedFunctionU<Impl<T>, absl::IntervalOpenClosedTag, T, T>(   \
+      register_fn);                                                    \
+  HandleFuzzedFunctionU<Impl<T>, absl::IntervalClosedOpenTag, T, T>(   \
+      register_fn);                                                    \
+  HandleFuzzedFunctionU<Impl<T>, absl::IntervalClosedClosedTag, T, T>( \
+      register_fn);                                                    \
+  HandleFuzzedFunctionU<Impl<T>, T, T>(register_fn)
+
+  X_ALL(ImplUniform);
+
+#undef X_IMPL_T
+#define X_IMPL_T(T, Impl) HandleFuzzedFunctionU<Impl<T>>(register_fn)
+
+  X_UINT(ImplUniform);
+
+#undef X_IMPL_T
+}
+
+}  // namespace fuzztest::internal
diff --git a/fuzztest/internal/register_fuzzing_mocks.h b/fuzztest/internal/register_absl_fuzzing_mocks.h
similarity index 64%
rename from fuzztest/internal/register_fuzzing_mocks.h
rename to fuzztest/internal/register_absl_fuzzing_mocks.h
index 7726c22..9a3d8b5 100644
--- a/fuzztest/internal/register_fuzzing_mocks.h
+++ b/fuzztest/internal/register_absl_fuzzing_mocks.h
@@ -12,22 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef FUZZTEST_FUZZTEST_INTERNAL_REGISTER_FUZZING_MOCKS_H_
-#define FUZZTEST_FUZZTEST_INTERNAL_REGISTER_FUZZING_MOCKS_H_
-
-#include <cstdint>
+#ifndef FUZZTEST_FUZZTEST_INTERNAL_REGISTER_ABSL_FUZZING_MOCKS_H_
+#define FUZZTEST_FUZZTEST_INTERNAL_REGISTER_ABSL_FUZZING_MOCKS_H_
 
 #include "absl/base/fast_type_id.h"
 #include "absl/functional/function_ref.h"
-#include "absl/types/span.h"
+#include "./fuzztest/internal/fuzzing_mock_stream.h"
 
 namespace fuzztest::internal {
 
-// TypeErasedFuzzFunctionT(datastream, args_tuple, result) is a type erased
-// function pointer for use with absl::MockingBitGen and fuzztest mocking.
-using TypeErasedFuzzFunctionT = void (*)(absl::Span<const uint8_t>&, void*,
-                                         void*);
-
 // Registers the fuzzing functions for Abseil distributions.
 void RegisterAbslRandomFuzzingMocks(
     absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
@@ -35,4 +28,4 @@
 
 }  // namespace fuzztest::internal
 
-#endif  // FUZZTEST_FUZZTEST_INTERNAL_REGISTER_FUZZING_MOCKS_H_
+#endif  // FUZZTEST_FUZZTEST_INTERNAL_REGISTER_ABSL_FUZZING_MOCKS_H_
diff --git a/fuzztest/internal/register_absl_fuzzing_mocks_test.cc b/fuzztest/internal/register_absl_fuzzing_mocks_test.cc
new file mode 100644
index 0000000..424dddc
--- /dev/null
+++ b/fuzztest/internal/register_absl_fuzzing_mocks_test.cc
@@ -0,0 +1,174 @@
+// Copyright 2026 Google LLC
+//
+// 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
+//
+//      http://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 <cstdint>
+#include <limits>
+
+#include "gtest/gtest.h"
+#include "absl/random/bit_gen_ref.h"
+#include "absl/random/random.h"
+#include "./fuzztest/fuzzing_bit_gen.h"
+
+namespace fuzztest {
+namespace {
+
+constexpr const uint8_t kControlStream[6] = {
+    0,  // data stream variate
+    1,  // lcg variate
+    2,  // min
+    3,  // max
+    4,  // mean
+    5,  // alternate variate
+};
+
+constexpr uint8_t kDataStream[40] = {
+    1,    2,    3,    4,    5,    6,    7,  8,   //
+    42,   42,   42,   42,   42,   42,   42, 42,  //
+    50,   60,   70,   80,   10,   20,   30, 40,  //
+    0x7f, 0x0f, 0x6f, 0x0f, 0x5f, 0x0f,
+};
+constexpr const uint64_t kSeedValue = 0x0807060504030201;
+
+// Tests for the absl/random distribution functions which use the
+// fuzztest::internal::RegisterAbslRandomFuzzingMocks() function.
+
+TEST(FuzzingBitGenTest, BernoulliDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_TRUE(absl::Bernoulli(ref, 0.5));
+  EXPECT_TRUE(absl::Bernoulli(ref, 0.5));   // lcg
+  EXPECT_FALSE(absl::Bernoulli(ref, 0.5));  // min
+  EXPECT_TRUE(absl::Bernoulli(ref, 0.5));   // max
+  EXPECT_TRUE(absl::Bernoulli(ref, 0.6));   // mean
+}
+
+TEST(FuzzingBitGenTest, BetaDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_DOUBLE_EQ(absl::Beta<double>(ref, 2.0, 2.0), 0.081234075853663129);
+  EXPECT_DOUBLE_EQ(absl::Beta<double>(ref, 2.0, 2.0),
+                   0.65593732986573283);                     // lcg
+  EXPECT_DOUBLE_EQ(absl::Beta<double>(ref, 2.0, 2.0), 0.0);  // min
+  EXPECT_DOUBLE_EQ(absl::Beta<double>(ref, 2.0, 2.0), 1.0);  // max
+  EXPECT_DOUBLE_EQ(absl::Beta<double>(ref, 2.0, 2.0), 0.5);  // mean
+}
+
+TEST(FuzzingBitGenTest, ExponentialDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_DOUBLE_EQ(absl::Exponential<double>(ref, 2.0), 0.015929665930210696);
+  EXPECT_DOUBLE_EQ(absl::Exponential<double>(ref, 2.0),  // lcg
+                   0.62503166008171429);
+  EXPECT_DOUBLE_EQ(absl::Exponential<double>(ref, 2.0), 0.0);  // min
+  EXPECT_DOUBLE_EQ(absl::Exponential<double>(ref, 2.0),        // max
+                   std::numeric_limits<double>::max());
+  EXPECT_DOUBLE_EQ(absl::Exponential<double>(ref, 2.0), 0.5);  // mean
+  EXPECT_DOUBLE_EQ(absl::Exponential<double>(ref, 2.0),        // alt
+                   2.9609063397732257e+307);
+}
+
+TEST(FuzzingBitGenTest, GaussianDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_DOUBLE_EQ(absl::Gaussian<double>(ref, 10.0, 1.0), 10.215901634330736);
+  EXPECT_DOUBLE_EQ(absl::Gaussian<double>(ref, 10.0, 1.0),
+                   9.3160235046777462);                     // lcg
+  EXPECT_DOUBLE_EQ(absl::Gaussian<double>(ref, 10.0, 1.0),  // min
+                   -std::numeric_limits<double>::max());
+  EXPECT_DOUBLE_EQ(absl::Gaussian<double>(ref, 10.0, 1.0),  // max
+                   std::numeric_limits<double>::max());
+  EXPECT_DOUBLE_EQ(absl::Gaussian<double>(ref, 10.0, 1.0), 10.0);  // mean
+  EXPECT_DOUBLE_EQ(absl::Gaussian<double>(ref, 10.0, 1.0),         // alt
+                   3.2941176470588234);
+}
+
+TEST(FuzzingBitGenTest, LogUniformDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_EQ(absl::LogUniform<int>(ref, 10, 1000), 10);
+  EXPECT_EQ(absl::LogUniform<int>(ref, 10, 1000), 11);    // lcg
+  EXPECT_EQ(absl::LogUniform<int>(ref, 10, 1000), 10);    // min
+  EXPECT_EQ(absl::LogUniform<int>(ref, 10, 1000), 1000);  // max
+  EXPECT_EQ(absl::LogUniform<int>(ref, 10, 1000), 214);   // mean (approx)
+  EXPECT_EQ(absl::LogUniform<int>(ref, 10, 1000), 894);   // alt
+}
+
+TEST(FuzzingBitGenTest, PoissonDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_EQ(absl::Poisson<int>(ref, 10.0), 2);
+  EXPECT_EQ(absl::Poisson<int>(ref, 10.0), 9);           // lcg
+  EXPECT_EQ(absl::Poisson<int>(ref, 10.0), 0);           // min
+  EXPECT_EQ(absl::Poisson<int>(ref, 10.0), 2147483647);  // max
+  EXPECT_EQ(absl::Poisson<int>(ref, 10.0), 10);          // mean
+  EXPECT_EQ(absl::Poisson<int>(ref, 10.0), 0);           // alt
+}
+
+TEST(FuzzingBitGenTest, ZipfDistributionUsesMock) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_EQ(absl::Zipf<int>(ref, 100, 2.0, 1.0), 15);
+  EXPECT_EQ(absl::Zipf<int>(ref, 100, 2.0, 1.0), 0);    // lcg
+  EXPECT_EQ(absl::Zipf<int>(ref, 100, 2.0, 1.0), 0);    // min
+  EXPECT_EQ(absl::Zipf<int>(ref, 100, 2.0, 1.0), 100);  // max
+  EXPECT_NE(absl::Zipf<int>(ref, 100, 2.0, 1.0), -1);   // (unused)
+  EXPECT_EQ(absl::Zipf<int>(ref, 100, 2.0, 1.0), 50);   // alt
+}
+
+TEST(FuzzingBitGenTest, UniformDistributionUInt) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_EQ(absl::Uniform<uint16_t>(ref), 0x0201);
+  EXPECT_EQ(absl::Uniform<uint16_t>(ref), 34107);  // lcg
+  EXPECT_EQ(absl::Uniform<uint16_t>(ref), 0);      // min
+  EXPECT_EQ(absl::Uniform<uint16_t>(ref),
+            std::numeric_limits<uint16_t>::max());  // max
+  EXPECT_EQ(absl::Uniform<uint16_t>(ref),
+            std::numeric_limits<uint16_t>::max() / 2);  // mean
+}
+
+TEST(FuzzingBitGenTest, UniformDistributionInt) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_EQ(absl::Uniform<uint64_t>(ref, 0, 100), 3);
+  EXPECT_EQ(absl::Uniform<uint64_t>(ref, 0, 100), 71);  // lcg
+  EXPECT_EQ(absl::Uniform<uint64_t>(ref, 0, 100), 0);   // min
+  EXPECT_EQ(absl::Uniform<uint64_t>(ref, 0, 100), 99);  // max
+  EXPECT_EQ(absl::Uniform<uint64_t>(ref, 0, 100), 49);  // mean
+  EXPECT_EQ(absl::Uniform<uint64_t>(ref, 0, 100), 42);  // alt
+}
+
+TEST(FuzzingBitGenTest, UniformDistributionReal) {
+  FuzzingBitGen bitgen(kDataStream, kControlStream, kSeedValue);
+  absl::BitGenRef ref(bitgen);
+
+  EXPECT_DOUBLE_EQ(absl::Uniform<double>(ref, 0.0, 100.0), 3.1357170319108034);
+  EXPECT_DOUBLE_EQ(absl::Uniform<double>(ref, 0.0, 100.0),  // lcg
+                   71.351334409602003);
+  EXPECT_DOUBLE_EQ(absl::Uniform<double>(ref, 0.0, 100.0), 0.0);   // min
+  EXPECT_LT(absl::Uniform<double>(ref, 0.0, 100.0), 100.0);        // max
+  EXPECT_DOUBLE_EQ(absl::Uniform<double>(ref, 0.0, 100.0), 50.0);  // mean
+}
+
+}  // namespace
+}  // namespace fuzztest
diff --git a/fuzztest/internal/register_fuzzing_mocks.cc b/fuzztest/internal/register_fuzzing_mocks.cc
deleted file mode 100644
index 6bb05df..0000000
--- a/fuzztest/internal/register_fuzzing_mocks.cc
+++ /dev/null
@@ -1,464 +0,0 @@
-// Copyright 2025 Google LLC
-//
-// 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
-//
-//      http://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 "./fuzztest/internal/register_fuzzing_mocks.h"
-
-#include <algorithm>
-#include <climits>
-#include <cmath>
-#include <cstddef>
-#include <cstdint>
-#include <cstring>
-#include <limits>
-#include <tuple>
-#include <type_traits>
-
-#include "absl/base/fast_type_id.h"
-#include "absl/functional/function_ref.h"
-#include "absl/random/bernoulli_distribution.h"
-#include "absl/random/beta_distribution.h"
-#include "absl/random/distributions.h"
-#include "absl/random/exponential_distribution.h"
-#include "absl/random/gaussian_distribution.h"
-#include "absl/random/log_uniform_int_distribution.h"
-#include "absl/random/poisson_distribution.h"
-#include "absl/random/zipf_distribution.h"
-#include "absl/types/span.h"
-#include "./common/logging.h"
-
-namespace fuzztest::internal {
-namespace {
-
-// Reference type to consume bytes from a data stream; these are used by
-// the fuzzing bitgen distribution implementations.
-struct DataStreamConsumer {
-  // This is a reference to the fuzzing data stream since the mutations
-  // (src.remove_prefix(...), etc.) are applied to the source stream.
-  absl::Span<const uint8_t>& src;
-
-  // Consumes up to num_bytes from the head of the data stream.
-  size_t ConsumeHead(void* destination, size_t num_bytes) {
-    num_bytes = std::min(num_bytes, src.size());
-    std::memcpy(destination, src.data(), num_bytes);
-    src.remove_prefix(num_bytes);
-    return num_bytes;
-  }
-
-  // Consumes up to num_bytes from the tail of the data stream.
-  size_t ConsumeTail(void* destination, size_t num_bytes) {
-    num_bytes = std::min(num_bytes, src.size());
-    std::memcpy(destination, src.data() + src.size() - num_bytes, num_bytes);
-    src.remove_suffix(num_bytes);
-    return num_bytes;
-  }
-
-  // Consumes a T from the head of the data stream.
-  template <typename T>
-  T ConsumeHead() {
-    std::conditional_t<std::is_same_v<T, bool>, uint8_t, T> x{};
-    ConsumeHead(&x, sizeof(x));
-    if constexpr (std::is_same_v<T, bool>) {
-      return static_cast<bool>(x & 1);
-    } else {
-      return x;
-    }
-  }
-
-  // Consumes a T from the tail of the data stream.
-  template <typename T>
-  T ConsumeTail() {
-    std::conditional_t<std::is_same_v<T, bool>, uint8_t, T> x{};
-    ConsumeTail(&x, sizeof(x));
-    if constexpr (std::is_same_v<T, bool>) {
-      return static_cast<bool>(x & 1);
-    } else {
-      return x;
-    }
-  }
-
-  // Returns a real value in the range [0.0, 1.0].
-  template <typename T>
-  T ConsumeProbability() {
-    static_assert(std::is_floating_point_v<T> && sizeof(T) <= sizeof(uint64_t),
-                  "A floating point type is required.");
-    using IntegralType =
-        typename std::conditional_t<(sizeof(T) <= sizeof(uint32_t)), uint32_t,
-                                    uint64_t>;
-    auto int_value = ConsumeTail<IntegralType>();
-    return static_cast<T>(int_value) /
-           static_cast<T>(std::numeric_limits<IntegralType>::max());
-  }
-
-  // Returns a value in the closed-closed range [min, max].
-  template <typename T>
-  T ConsumeValueInRange(T min, T max) {
-    FUZZTEST_CHECK_LE(min, max);
-
-    if (min == max) return min;
-
-    // Return the min or max value more frequently.
-    uint8_t byte = ConsumeHead<uint8_t>();
-    if (byte == 0) {
-      return min;
-    } else if (byte == 1) {
-      return max;
-    }
-    byte >>= 1;
-
-    return ConsumeValueInRangeImpl<T>(min, max, byte);
-  }
-
- private:
-  // Returns a real value in the range [min, max]
-  template <typename T>
-  std::enable_if_t<std::is_floating_point_v<T>, T>  //
-  ConsumeValueInRangeImpl(T min, T max, uint8_t byte) {
-    static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported float type.");
-    // Returns a floating point value in the given range by consuming bytes
-    // from the input data. If there's no input data left, returns |min|. Note
-    // that |min| must be less than or equal to |max|.
-    T range = .0;
-    T result = min;
-    constexpr T zero(.0);
-    if (max > zero && min < zero && max > min + std::numeric_limits<T>::max()) {
-      // The diff |max - min| would overflow the given floating point type.
-      // Use the half of the diff as the range and consume a bool to decide
-      // whether the result is in the first of the second part of the diff.
-      range = (max / 2.0) - (min / 2.0);
-      if (byte & 1) {
-        result += range;
-      }
-    } else {
-      range = max - min;
-    }
-    return result + range * ConsumeProbability<T>();
-  }
-
-  // Returns an integral value in the range [min, max]
-  template <typename T>
-  std::enable_if_t<std::is_integral_v<T>, T>  //
-  ConsumeValueInRangeImpl(T min, T max, uint8_t) {
-    static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type.");
-
-    // Use the biggest type possible to hold the range and the result.
-    uint64_t range = static_cast<uint64_t>(max) - static_cast<uint64_t>(min);
-    uint64_t result = 0;
-    size_t offset = 0;
-    while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 &&
-           !src.empty()) {
-      uint8_t byte = src.back();
-      src.remove_suffix(1);
-      result = (result << CHAR_BIT) | byte;
-      offset += CHAR_BIT;
-    }
-
-    // Avoid division by 0, in case |range + 1| results in overflow.
-    if (range != std::numeric_limits<decltype(range)>::max()) {
-      result = result % (range + 1);
-    }
-
-    return static_cast<T>(static_cast<uint64_t>(min) + result);
-  }
-};
-
-// -----------------------------------------------------------------------------
-
-// Bernoulli
-struct ImplBernoulli : public DataStreamConsumer {
-  using DistrT = absl::bernoulli_distribution;
-  using ArgTupleT = std::tuple<double>;
-  using ResultT = bool;
-
-  ResultT operator()(double p) {
-    // Just generate a boolean; mostly ignoring p.
-    // The 0/1 cases are special cased to avoid returning false on constants.
-    if (p == 0.0) {
-      return false;
-    } else if (p == 1.0) {
-      return true;
-    } else {
-      return ConsumeHead<bool>();
-    }
-  }
-};
-
-// Beta
-template <typename RealType>
-struct ImplBeta : public DataStreamConsumer {
-  using DistrT = absl::beta_distribution<RealType>;
-  using ArgTupleT = std::tuple<RealType, RealType>;
-  using ResultT = RealType;
-
-  ResultT operator()(RealType a, RealType b) {
-    if (!src.empty()) {
-      auto x = ConsumeTail<RealType>();
-      if (std::isfinite(x)) {
-        return x;
-      }
-    }
-    return a / (a + b);  // mean
-  }
-};
-
-// Exponential
-template <typename RealType>
-struct ImplExponential : public DataStreamConsumer {
-  using DistrT = absl::exponential_distribution<RealType>;
-  using ArgTupleT = std::tuple<RealType>;
-  using ResultT = RealType;
-
-  ResultT operator()(RealType lambda) {
-    if (!src.empty()) {
-      auto x = ConsumeTail<RealType>();
-      if (std::isfinite(x)) {
-        return x;
-      }
-    }
-    return RealType{1} / lambda;  // mean
-  }
-};
-
-// Gaussian
-template <typename RealType>
-struct ImplGaussian : public DataStreamConsumer {
-  using DistrT = absl::gaussian_distribution<RealType>;
-  using ArgTupleT = std::tuple<RealType, RealType>;
-  using ResultT = RealType;
-
-  ResultT operator()(RealType mean, RealType sigma) {
-    if (src.empty()) return mean;
-    const auto ten_sigma = sigma * 10;
-    RealType min = mean - ten_sigma;
-    RealType max = mean + ten_sigma;
-    return ConsumeValueInRange<RealType>(min, max);
-  }
-};
-
-// LogUniform
-template <typename IntType>
-struct ImplLogUniform : public DataStreamConsumer {
-  using DistrT = absl::log_uniform_int_distribution<IntType>;
-  using ArgTupleT = std::tuple<IntType, IntType, IntType>;
-  using ResultT = IntType;
-
-  ResultT operator()(IntType a, IntType b, IntType) {
-    if (src.empty()) return a;
-    return ConsumeValueInRange<IntType>(a, b);
-  }
-};
-
-// Poisson
-template <typename IntType>
-struct ImplPoisson : public DataStreamConsumer {
-  using DistrT = absl::poisson_distribution<IntType>;
-  using ArgTupleT = std::tuple<double>;
-  using ResultT = IntType;
-
-  ResultT operator()(double) {
-    if (src.empty()) return 0;
-    return ConsumeValueInRange<IntType>(0, std::numeric_limits<IntType>::max());
-  }
-};
-
-// Zipf
-template <typename IntType>
-struct ImplZipf : public DataStreamConsumer {
-  using DistrT = absl::zipf_distribution<IntType>;
-  using ArgTupleT = std::tuple<IntType, double, double>;
-  using ResultT = IntType;
-
-  ResultT operator()(IntType a, double, double) {
-    if (src.empty()) return 0;
-    return ConsumeValueInRange<IntType>(0, a);
-  }
-};
-
-// Uniform
-template <typename R>
-struct ImplUniform : public DataStreamConsumer {
-  using DistrT = absl::random_internal::UniformDistributionWrapper<R>;
-  using ResultT = R;
-
-  ResultT operator()(absl::IntervalClosedClosedTag, R min, R max) {
-    if (src.empty()) return min;
-    return ConsumeValueInRange<R>(min, max);
-  }
-
-  ResultT operator()(absl::IntervalClosedOpenTag, R min, R max) {
-    if (src.empty()) return min;
-    if constexpr (std::is_floating_point_v<R>) {
-      max = std::nexttoward(max, min);
-      return ConsumeValueInRange<R>(min, max);
-    } else {
-      max--;
-      return ConsumeValueInRange<R>(min, max);
-    }
-  }
-
-  ResultT operator()(absl::IntervalOpenOpenTag, R min, R max) {
-    if (src.empty()) return min;
-    if constexpr (std::is_floating_point_v<R>) {
-      min = std::nexttoward(min, max);
-      max = std::nexttoward(max, min);
-      return ConsumeValueInRange<R>(min, max);
-    } else {
-      min++;
-      max--;
-      return ConsumeValueInRange<R>(min, max);
-    }
-  }
-
-  ResultT operator()(absl::IntervalOpenClosedTag, R min, R max) {
-    if (src.empty()) return min;
-    if constexpr (std::is_floating_point_v<R>) {
-      min = std::nexttoward(min, max);
-      return ConsumeValueInRange<R>(min, max);
-    } else {
-      min++;
-      return ConsumeValueInRange<R>(min, max);
-    }
-  }
-
-  ResultT operator()(R min, R max) {
-    return operator()(absl::IntervalClosedOpen, min, max);
-  }
-
-  ResultT operator()() {
-    static_assert(std::is_unsigned_v<R>);
-    if (src.empty()) return 0;
-    return ConsumeTail<R>();
-  }
-};
-
-// -----------------------------------------------------------------------------
-
-// InvokeFuzzFunction is a type-erased function pointer which is responsible for
-// casting the args_tuple and result parameters to the correct types and then
-// invoking the implementation functor. It is important that the ArgsTupleT and
-// ResultT types match the types of the distribution and the implementation
-// functions, so the HandleFuzzedFunction overloads are used to determine the
-// correct types.
-template <typename FuzzFunctionT, typename ResultT, typename ArgTupleT>
-void InvokeFuzzFunction(absl::Span<const uint8_t>& src, void* args_tuple,
-                        void* result) {
-  FuzzFunctionT fn{src};
-  *static_cast<ResultT*>(result) =
-      absl::apply(fn, *static_cast<ArgTupleT*>(args_tuple));
-}
-
-template <typename FuzzFunctionT>
-void HandleFuzzedFunctionX(
-    absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
-        register_fn) {
-  using DistrT = typename FuzzFunctionT::DistrT;
-  using ArgTupleT = typename FuzzFunctionT::ArgTupleT;
-  using ResultT = typename FuzzFunctionT::ResultT;
-  using KeyT = ResultT(DistrT, ArgTupleT);
-
-  register_fn(absl::FastTypeId<KeyT>(),
-              &InvokeFuzzFunction<FuzzFunctionT, ResultT, ArgTupleT>);
-}
-
-template <typename FuzzFunctionT, typename... Args>
-void HandleFuzzedFunctionU(
-    absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
-        register_fn) {
-  using DistrT = typename FuzzFunctionT::DistrT;
-  using ArgTupleT = std::tuple<std::decay_t<Args>...>;
-  using ResultT = typename FuzzFunctionT::ResultT;
-  using KeyT = ResultT(DistrT, ArgTupleT);
-
-  register_fn(absl::FastTypeId<KeyT>(),
-              &InvokeFuzzFunction<FuzzFunctionT, ResultT, ArgTupleT>);
-}
-
-// -----------------------------------------------------------------------------
-// X_ macros to invoke X_IMPL_T macros for each type.
-// -----------------------------------------------------------------------------
-
-#define X_SINT(Impl)                                  \
-  if constexpr (std::is_signed_v<char> &&             \
-                !std::is_same_v<char, signed char>) { \
-    X_IMPL_T(char, Impl);                             \
-  }                                                   \
-  X_IMPL_T(signed char, Impl);                        \
-  X_IMPL_T(short, Impl);     /*NOLINT*/               \
-  X_IMPL_T(long, Impl);      /*NOLINT*/               \
-  X_IMPL_T(long long, Impl); /*NOLINT*/               \
-  X_IMPL_T(int, Impl)
-
-#define X_UINT(Impl)                                    \
-  if constexpr (std::is_unsigned_v<char> &&             \
-                !std::is_same_v<char, unsigned char>) { \
-    X_IMPL_T(char, Impl);                               \
-  }                                                     \
-  X_IMPL_T(unsigned char, Impl);                        \
-  X_IMPL_T(unsigned short, Impl);     /*NOLINT*/        \
-  X_IMPL_T(unsigned long, Impl);      /*NOLINT*/        \
-  X_IMPL_T(unsigned long long, Impl); /*NOLINT*/        \
-  X_IMPL_T(unsigned int, Impl)
-
-#define X_REAL(Impl)     \
-  X_IMPL_T(float, Impl); \
-  X_IMPL_T(double, Impl)
-
-#define X_XINT(Impl) \
-  X_SINT(Impl);      \
-  X_UINT(Impl)
-
-#define X_ALL(Impl) \
-  X_SINT(Impl);     \
-  X_UINT(Impl);     \
-  X_REAL(Impl)
-
-}  // namespace
-
-// Registers the fuzzing functions into the fuzztest mock map.
-void RegisterAbslRandomFuzzingMocks(
-    absl::FunctionRef<void(absl::FastTypeIdType, TypeErasedFuzzFunctionT)>
-        register_fn) {
-#define X_IMPL_T(T, Impl) HandleFuzzedFunctionX<Impl<T>>(register_fn)
-
-  HandleFuzzedFunctionX<ImplBernoulli>(register_fn);
-
-  X_REAL(ImplBeta);
-  X_REAL(ImplExponential);
-  X_REAL(ImplGaussian);
-  X_XINT(ImplLogUniform);
-  X_XINT(ImplPoisson);
-  X_XINT(ImplZipf);
-
-#undef X_IMPL_T
-#define X_IMPL_T(T, Impl)                                              \
-  HandleFuzzedFunctionU<Impl<T>, absl::IntervalOpenOpenTag, T, T>(     \
-      register_fn);                                                    \
-  HandleFuzzedFunctionU<Impl<T>, absl::IntervalOpenClosedTag, T, T>(   \
-      register_fn);                                                    \
-  HandleFuzzedFunctionU<Impl<T>, absl::IntervalClosedOpenTag, T, T>(   \
-      register_fn);                                                    \
-  HandleFuzzedFunctionU<Impl<T>, absl::IntervalClosedClosedTag, T, T>( \
-      register_fn);                                                    \
-  HandleFuzzedFunctionU<Impl<T>, T, T>(register_fn)
-
-  X_ALL(ImplUniform);
-
-#undef X_IMPL_T
-#define X_IMPL_T(T, Impl) HandleFuzzedFunctionU<Impl<T>>(register_fn)
-
-  X_UINT(ImplUniform);
-
-#undef X_IMPL_T
-}
-
-}  // namespace fuzztest::internal