Unify fuzzer random number generation into a single class

BUG=tint:1098

Change-Id: I84931804515487d931bbbb5f0d5239d03ca76dfc
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/63300
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Alastair Donaldson <afdx@google.com>
diff --git a/fuzzers/BUILD.gn b/fuzzers/BUILD.gn
index 89857d0..9295aef 100644
--- a/fuzzers/BUILD.gn
+++ b/fuzzers/BUILD.gn
@@ -66,6 +66,8 @@
     ]
 
     sources = [
+      "random_generator.cc",
+      "random_generator.h",
       "tint_common_fuzzer.cc",
       "tint_common_fuzzer.h",
     ]
diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt
index b24229f..6b962d7 100644
--- a/fuzzers/CMakeLists.txt
+++ b/fuzzers/CMakeLists.txt
@@ -17,6 +17,8 @@
     ${NAME}.cc
     cli.cc
     cli.h
+    random_generator.cc
+    random_generator.h
     tint_common_fuzzer.cc
     tint_common_fuzzer.h
     tint_init_fuzzer.cc
diff --git a/fuzzers/random_generator.cc b/fuzzers/random_generator.cc
new file mode 100644
index 0000000..18b88fe
--- /dev/null
+++ b/fuzzers/random_generator.cc
@@ -0,0 +1,89 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     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 "fuzzers/random_generator.h"
+
+#include <algorithm>
+#include <cassert>
+#include <vector>
+
+namespace tint {
+namespace fuzzers {
+
+namespace {
+
+/// Generate integer from uniform distribution
+/// @tparam I - integer type
+/// @param engine - random number engine to use
+/// @param lower - Lower bound of integer generated
+/// @param upper - Upper bound of integer generated
+/// @returns i, where lower <= i < upper
+template <typename I>
+I RandomUInt(std::mt19937* engine, I lower, I upper) {
+  assert(lower < upper && "|lower| must be stictly less than |upper|");
+
+  return std::uniform_int_distribution<I>(lower, upper - 1)(*engine);
+}
+
+}  // namespace
+
+RandomGenerator::RandomGenerator(uint32_t seed) : engine_(seed) {}
+
+uint32_t RandomGenerator::GetUInt32(uint32_t lower, uint32_t upper) {
+  return RandomUInt(&engine_, lower, upper);
+}
+
+uint32_t RandomGenerator::GetUInt32(uint32_t bound) {
+  assert(bound > 0 && "|bound| must be greater than 0");
+  return RandomUInt(&engine_, 0u, bound);
+}
+
+uint64_t RandomGenerator::GetUInt64(uint64_t lower, uint64_t upper) {
+  return RandomUInt(&engine_, lower, upper);
+}
+
+uint64_t RandomGenerator::GetUInt64(uint64_t bound) {
+  assert(bound > 0 && "|bound| must be greater than 0");
+  return RandomUInt(&engine_, static_cast<uint64_t>(0), bound);
+}
+
+uint8_t RandomGenerator::GetByte() {
+  return std::independent_bits_engine<std::mt19937, 8, uint8_t>(engine_)();
+}
+
+uint32_t RandomGenerator::Get4Bytes() {
+  return std::independent_bits_engine<std::mt19937, 32, uint32_t>(engine_)();
+}
+
+std::vector<uint8_t> RandomGenerator::GetNBytes(size_t n) {
+  std::vector<uint8_t> result(n);
+  std::generate(
+      std::begin(result), std::end(result),
+      std::independent_bits_engine<std::mt19937, 8, uint8_t>(engine_));
+  return result;
+}
+
+bool RandomGenerator::GetBool() {
+  return RandomUInt(&engine_, 0u, 2u);
+}
+
+bool RandomGenerator::GetWeightedBool(uint32_t percentage) {
+  static const uint32_t kMaxPercentage = 100;
+  assert(percentage <= kMaxPercentage &&
+         "|percentage| needs to be within [0, 100]");
+  return RandomUInt(&engine_, 0u, kMaxPercentage) < percentage;
+}
+
+}  // namespace fuzzers
+}  // namespace tint
diff --git a/fuzzers/random_generator.h b/fuzzers/random_generator.h
new file mode 100644
index 0000000..c90a3e7
--- /dev/null
+++ b/fuzzers/random_generator.h
@@ -0,0 +1,87 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     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 FUZZERS_RANDOM_GENERATOR_H_
+#define FUZZERS_RANDOM_GENERATOR_H_
+
+#include <random>
+#include <vector>
+
+namespace tint {
+namespace fuzzers {
+
+/// Pseudo random generator utility class for fuzzing
+class RandomGenerator {
+ public:
+  /// @brief Initializes the internal engine
+  /// @param seed - seed value passed to engine
+  explicit RandomGenerator(uint32_t seed);
+  ~RandomGenerator() {}
+
+  /// Get uint32_t value from uniform distribution.
+  /// @param lower - lower bound of integer generated
+  /// @param upper - upper bound of integer generated
+  /// @returns i, where lower <= i < upper
+  uint32_t GetUInt32(uint32_t lower, uint32_t upper);
+
+  /// Get uint32_t value from uniform distribution.
+  /// @param bound - Upper bound of integer generated
+  /// @returns i, where 0 <= i < bound
+  uint32_t GetUInt32(uint32_t bound);
+
+  /// Get uint32_t value from uniform distribution.
+  /// @param lower - lower bound of integer generated
+  /// @param upper - upper bound of integer generated
+  /// @returns i, where lower <= i < upper
+  uint64_t GetUInt64(uint64_t lower, uint64_t upper);
+
+  /// Get uint64_t value from uniform distribution.
+  /// @param bound - Upper bound of integer generated
+  /// @returns i, where 0 <= i < bound
+  uint64_t GetUInt64(uint64_t bound);
+
+  /// Get 1 byte of pseudo-random data
+  /// Should be more efficient then calling GetNBytes(1);
+  /// @returns 1-byte of random data
+  uint8_t GetByte();
+
+  /// Get 4 bytes of pseudo-random data
+  /// Should be more efficient then calling GetNBytes(4);
+  /// @returns 4-bytes of random data
+  uint32_t Get4Bytes();
+
+  /// Get N bytes of pseudo-random data
+  /// @param n - number of bytes of data to generate
+  /// @returns |N|-bytes of random data as vector
+  std::vector<uint8_t> GetNBytes(size_t n);
+
+  /// Get random bool with even odds
+  /// @returns true 50% of the time and false %50 of time.
+  bool GetBool();
+
+  /// Get random bool with weighted odds
+  /// @param percentage - likelihood of true being returned
+  /// @returns true |percentage|% of the time, and false (100 - |percentage|)%
+  /// of the time.
+  bool GetWeightedBool(uint32_t percentage);
+
+ private:
+  std::mt19937 engine_;
+
+};  // class RandomGenerator
+
+}  // namespace fuzzers
+}  // namespace tint
+
+#endif  // FUZZERS_RANDOM_GENERATOR_H_
diff --git a/fuzzers/tint_ast_fuzzer/BUILD.gn b/fuzzers/tint_ast_fuzzer/BUILD.gn
index 888aca5..1b8a1f5 100644
--- a/fuzzers/tint_ast_fuzzer/BUILD.gn
+++ b/fuzzers/tint_ast_fuzzer/BUILD.gn
@@ -42,8 +42,6 @@
       "cli.cc",
       "cli.h",
       "fuzzer.cc",
-      "mt_rng.cc",
-      "mt_rng.h",
       "mutation.cc",
       "mutation.h",
       "mutation_finder.cc",
@@ -60,8 +58,6 @@
       "probability_context.cc",
       "probability_context.h",
       "protobufs/tint_ast_fuzzer.h",
-      "random_number_generator.cc",
-      "random_number_generator.h",
       "util.h",
     ]
   }
diff --git a/fuzzers/tint_ast_fuzzer/CMakeLists.txt b/fuzzers/tint_ast_fuzzer/CMakeLists.txt
index 854b08d..1dcb668 100644
--- a/fuzzers/tint_ast_fuzzer/CMakeLists.txt
+++ b/fuzzers/tint_ast_fuzzer/CMakeLists.txt
@@ -35,7 +35,7 @@
         COMMENT "Generate protobuf sources from proto definition file.")
 
 set(LIBTINT_AST_FUZZER_SOURCES
-        mt_rng.h
+        ../random_generator.h
         mutation.h
         mutation_finder.h
         mutation_finders/replace_identifiers.h
@@ -44,12 +44,11 @@
         node_id_map.h
         probability_context.h
         protobufs/tint_ast_fuzzer.h
-        random_number_generator.h
         util.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/tint_ast_fuzzer.pb.h)
 
 set(LIBTINT_AST_FUZZER_SOURCES ${LIBTINT_AST_FUZZER_SOURCES}
-        mt_rng.cc
+        ../random_generator.cc
         mutation.cc
         mutation_finder.cc
         mutation_finders/replace_identifiers.cc
@@ -57,7 +56,6 @@
         mutator.cc
         node_id_map.cc
         probability_context.cc
-        random_number_generator.cc
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/tint_ast_fuzzer.pb.cc)
 
 set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/tint_ast_fuzzer.pb.cc PROPERTIES COMPILE_FLAGS -w)
diff --git a/fuzzers/tint_ast_fuzzer/fuzzer.cc b/fuzzers/tint_ast_fuzzer/fuzzer.cc
index dcee8dd..cdb1693 100644
--- a/fuzzers/tint_ast_fuzzer/fuzzer.cc
+++ b/fuzzers/tint_ast_fuzzer/fuzzer.cc
@@ -15,8 +15,8 @@
 #include <cstddef>
 #include <cstdint>
 
+#include "fuzzers/random_generator.h"
 #include "fuzzers/tint_ast_fuzzer/cli.h"
-#include "fuzzers/tint_ast_fuzzer/mt_rng.h"
 #include "fuzzers/tint_ast_fuzzer/mutator.h"
 #include "fuzzers/tint_ast_fuzzer/override_cli_params.h"
 #include "fuzzers/tint_common_fuzzer.h"
@@ -54,8 +54,8 @@
   }
 
   // Run the mutator.
-  MtRng mt_rng(seed);
-  ProbabilityContext probability_context(&mt_rng);
+  RandomGenerator generator(seed);
+  ProbabilityContext probability_context(&generator);
   program = Mutate(std::move(program), &probability_context,
                    cli_params.enable_all_mutations,
                    cli_params.mutation_batch_size, nullptr);
diff --git a/fuzzers/tint_ast_fuzzer/mt_rng.cc b/fuzzers/tint_ast_fuzzer/mt_rng.cc
deleted file mode 100644
index 586b2d9..0000000
--- a/fuzzers/tint_ast_fuzzer/mt_rng.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     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 "fuzzers/tint_ast_fuzzer/mt_rng.h"
-
-#include <cassert>
-
-namespace tint {
-namespace fuzzers {
-namespace ast_fuzzer {
-namespace {
-
-template <typename T>
-T RandomUInt(std::mt19937* rng, T bound) {
-  assert(bound > 0 && "`bound` must be positive");
-  return std::uniform_int_distribution<T>(0, bound - 1)(*rng);
-}
-
-}  // namespace
-
-MtRng::MtRng(uint32_t seed) : rng_(seed) {}
-
-uint32_t MtRng::RandomUint32(uint32_t bound) {
-  return RandomUInt(&rng_, bound);
-}
-
-uint64_t MtRng::RandomUint64(uint64_t bound) {
-  return RandomUInt(&rng_, bound);
-}
-
-}  // namespace ast_fuzzer
-}  // namespace fuzzers
-}  // namespace tint
diff --git a/fuzzers/tint_ast_fuzzer/mt_rng.h b/fuzzers/tint_ast_fuzzer/mt_rng.h
deleted file mode 100644
index ebbddc4..0000000
--- a/fuzzers/tint_ast_fuzzer/mt_rng.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     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 FUZZERS_TINT_AST_FUZZER_MT_RNG_H_
-#define FUZZERS_TINT_AST_FUZZER_MT_RNG_H_
-
-#include <random>
-
-#include "fuzzers/tint_ast_fuzzer/random_number_generator.h"
-
-namespace tint {
-namespace fuzzers {
-namespace ast_fuzzer {
-
-/// The random number generator that uses STL's Mersenne Twister (std::mt19937)
-/// under the hood.
-class MtRng : public RandomNumberGenerator {
- public:
-  /// @brief Initializes this RNG with some `seed`.
-  /// @param seed - passed down to the `std::mt19937`.
-  explicit MtRng(uint32_t seed);
-
-  uint32_t RandomUint32(uint32_t bound) override;
-  uint64_t RandomUint64(uint64_t bound) override;
-
- private:
-  std::mt19937 rng_;
-};
-
-}  // namespace ast_fuzzer
-}  // namespace fuzzers
-}  // namespace tint
-
-#endif  // FUZZERS_TINT_AST_FUZZER_MT_RNG_H_
diff --git a/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc b/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc
index 3af0059..bf12407 100644
--- a/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc
+++ b/fuzzers/tint_ast_fuzzer/mutations/replace_identifier_test.cc
@@ -16,7 +16,6 @@
 
 #include "gtest/gtest.h"
 
-#include "fuzzers/tint_ast_fuzzer/mt_rng.h"
 #include "fuzzers/tint_ast_fuzzer/mutations/replace_identifier.h"
 #include "fuzzers/tint_ast_fuzzer/mutator.h"
 #include "fuzzers/tint_ast_fuzzer/probability_context.h"
diff --git a/fuzzers/tint_ast_fuzzer/mutator.h b/fuzzers/tint_ast_fuzzer/mutator.h
index 7746e6a..59aa5b7 100644
--- a/fuzzers/tint_ast_fuzzer/mutator.h
+++ b/fuzzers/tint_ast_fuzzer/mutator.h
@@ -20,7 +20,6 @@
 #include "fuzzers/tint_ast_fuzzer/node_id_map.h"
 #include "fuzzers/tint_ast_fuzzer/probability_context.h"
 #include "fuzzers/tint_ast_fuzzer/protobufs/tint_ast_fuzzer.h"
-#include "fuzzers/tint_ast_fuzzer/random_number_generator.h"
 
 #include "src/program.h"
 
diff --git a/fuzzers/tint_ast_fuzzer/probability_context.cc b/fuzzers/tint_ast_fuzzer/probability_context.cc
index 1b38a10..8db9557 100644
--- a/fuzzers/tint_ast_fuzzer/probability_context.cc
+++ b/fuzzers/tint_ast_fuzzer/probability_context.cc
@@ -14,6 +14,8 @@
 
 #include "fuzzers/tint_ast_fuzzer/probability_context.h"
 
+#include <cassert>
+
 namespace tint {
 namespace fuzzers {
 namespace ast_fuzzer {
@@ -23,15 +25,18 @@
 
 }  // namespace
 
-ProbabilityContext::ProbabilityContext(RandomNumberGenerator* rng)
-    : rng_(rng),
+ProbabilityContext::ProbabilityContext(RandomGenerator* generator)
+    : generator_(generator),
       chance_of_replacing_identifiers_(
-          RandomFromRange(kChanceOfReplacingIdentifiers)) {}
+          RandomFromRange(kChanceOfReplacingIdentifiers)) {
+  assert(generator != nullptr && "generator must not be nullptr");
+}
 
 uint32_t ProbabilityContext::RandomFromRange(
     std::pair<uint32_t, uint32_t> range) {
   assert(range.first <= range.second && "Range must be non-decreasing");
-  return range.first + rng_->RandomUint32(range.second - range.first + 1);
+  return generator_->GetUInt32(
+      range.first, range.second + 1);  // + 1 need since range is inclusive.
 }
 
 }  // namespace ast_fuzzer
diff --git a/fuzzers/tint_ast_fuzzer/probability_context.h b/fuzzers/tint_ast_fuzzer/probability_context.h
index 572f125..c96b7ba 100644
--- a/fuzzers/tint_ast_fuzzer/probability_context.h
+++ b/fuzzers/tint_ast_fuzzer/probability_context.h
@@ -18,7 +18,7 @@
 #include <utility>
 #include <vector>
 
-#include "fuzzers/tint_ast_fuzzer/random_number_generator.h"
+#include "fuzzers/random_generator.h"
 
 namespace tint {
 namespace fuzzers {
@@ -29,16 +29,21 @@
 class ProbabilityContext {
  public:
   /// Initializes this instance with a random number generator.
-  /// @param rng - may not be a `nullptr`. Must remain in scope as long as this
+  /// @param generator - must not be a `nullptr`. Must remain in scope as long
+  /// as this
   ///     instance exists.
-  explicit ProbabilityContext(RandomNumberGenerator* rng);
+  explicit ProbabilityContext(RandomGenerator* generator);
 
-  /// @copydoc RandomNumberGenerator::RandomBool
-  bool RandomBool() { return rng_->RandomBool(); }
+  /// Get random bool with even odds
+  /// @returns true 50% of the time and false %50 of time.
+  bool RandomBool() { return generator_->GetBool(); }
 
-  /// @copydoc RandomNumberGenerator::ChoosePercentage
+  /// Get random bool with weighted odds
+  /// @param percentage - likelihood of true being returned
+  /// @returns true |percentage|% of the time, and false (100 - |percentage|)%
+  /// of the time.
   bool ChoosePercentage(uint32_t percentage) {
-    return rng_->ChoosePercentage(percentage);
+    return generator_->GetWeightedBool(percentage);
   }
 
   /// Returns a random value in the range `[0; arr.size())`.
@@ -47,7 +52,7 @@
   /// @return the random index in the `arr`.
   template <typename T>
   size_t GetRandomIndex(const std::vector<T>& arr) {
-    return static_cast<size_t>(rng_->RandomUint64(arr.size()));
+    return static_cast<size_t>(generator_->GetUInt64(arr.size()));
   }
 
   /// @return the probability of replacing some identifier with some other one.
@@ -60,7 +65,7 @@
   /// @return an random number in the range `[a; b]`.
   uint32_t RandomFromRange(std::pair<uint32_t, uint32_t> range);
 
-  RandomNumberGenerator* rng_;
+  RandomGenerator* generator_;
 
   uint32_t chance_of_replacing_identifiers_;
 };
diff --git a/fuzzers/tint_ast_fuzzer/random_number_generator.cc b/fuzzers/tint_ast_fuzzer/random_number_generator.cc
deleted file mode 100644
index 6d32e38..0000000
--- a/fuzzers/tint_ast_fuzzer/random_number_generator.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     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 "fuzzers/tint_ast_fuzzer/random_number_generator.h"
-
-namespace tint {
-namespace fuzzers {
-namespace ast_fuzzer {
-
-RandomNumberGenerator::~RandomNumberGenerator() = default;
-
-bool RandomNumberGenerator::RandomBool() {
-  return RandomUint32(2);
-}
-
-bool RandomNumberGenerator::ChoosePercentage(uint32_t percentage) {
-  assert(percentage <= 100 && "|percentage| is invalid");
-  // 100 is used as a bound instead of 101 because otherwise it would be
-  // possible to return `false` when `percentage == 100` holds. This would
-  // happen when the result of `RandomUint32` is 100 as well.
-  return RandomUint32(100) < percentage;
-}
-
-}  // namespace ast_fuzzer
-}  // namespace fuzzers
-}  // namespace tint
diff --git a/fuzzers/tint_ast_fuzzer/random_number_generator.h b/fuzzers/tint_ast_fuzzer/random_number_generator.h
deleted file mode 100644
index 27b1b54..0000000
--- a/fuzzers/tint_ast_fuzzer/random_number_generator.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     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 FUZZERS_TINT_AST_FUZZER_RANDOM_NUMBER_GENERATOR_H_
-#define FUZZERS_TINT_AST_FUZZER_RANDOM_NUMBER_GENERATOR_H_
-
-#include <cassert>
-#include <cstdint>
-#include <vector>
-
-namespace tint {
-namespace fuzzers {
-namespace ast_fuzzer {
-
-/// Abstracts away the underlying algorithm that is used to generate random
-/// numbers.
-class RandomNumberGenerator {
- public:
-  /// Virtual destructor.
-  virtual ~RandomNumberGenerator();
-
-  /// @brief Compute a random `uint32_t` value in the range `[0; bound)`.
-  /// @param bound - the upper exclusive bound for the computed integer
-  ///     (must be positive).
-  /// @return the random number.
-  virtual uint32_t RandomUint32(uint32_t bound) = 0;
-
-  /// @brief Compute a random `uint64_t` value in the range `[0; bound)`.
-  /// @param bound - the upper exclusive bound for the computed integer
-  ///     (must be positive).
-  /// @return the random number.
-  virtual uint64_t RandomUint64(uint64_t bound) = 0;
-
-  /// @return a randomly generated boolean value.
-  bool RandomBool();
-
-  /// @param percentage - must be in the range `[0; 100]`.
-  /// @return `true` with `percentage` probability.
-  bool ChoosePercentage(uint32_t percentage);
-};
-
-}  // namespace ast_fuzzer
-}  // namespace fuzzers
-}  // namespace tint
-
-#endif  // FUZZERS_TINT_AST_FUZZER_RANDOM_NUMBER_GENERATOR_H_
diff --git a/fuzzers/tint_regex_fuzzer/CMakeLists.txt b/fuzzers/tint_regex_fuzzer/CMakeLists.txt
index d4ae7c1..bd3fd8c 100644
--- a/fuzzers/tint_regex_fuzzer/CMakeLists.txt
+++ b/fuzzers/tint_regex_fuzzer/CMakeLists.txt
@@ -21,7 +21,8 @@
 endfunction()
 
 set(LIBTINT_REGEX_FUZZER_SOURCES
-        util.h
+        ../random_generator.cc
+        ../random_generator.h
         wgsl_mutator.cc
         wgsl_mutator.h)
 
diff --git a/fuzzers/tint_regex_fuzzer/fuzzer.cc b/fuzzers/tint_regex_fuzzer/fuzzer.cc
index 5c527c3..06f73dd 100644
--- a/fuzzers/tint_regex_fuzzer/fuzzer.cc
+++ b/fuzzers/tint_regex_fuzzer/fuzzer.cc
@@ -16,12 +16,11 @@
 #include <cstddef>
 #include <cstdint>
 
+#include "fuzzers/random_generator.h"
 #include "fuzzers/tint_common_fuzzer.h"
 #include "fuzzers/tint_regex_fuzzer/cli.h"
 #include "fuzzers/tint_regex_fuzzer/override_cli_params.h"
-#include "fuzzers/tint_regex_fuzzer/util.h"
 #include "fuzzers/tint_regex_fuzzer/wgsl_mutator.h"
-
 #include "src/reader/wgsl/parser.h"
 #include "src/writer/wgsl/generator.h"
 
@@ -57,13 +56,13 @@
                                           unsigned seed) {
   std::string wgsl_code(data, data + size);
   const std::vector<std::string> delimiters{";"};
-  std::mt19937 generator(seed);
+  RandomGenerator generator(seed);
 
   std::string delimiter =
-      delimiters[GetRandomIntFromRange(0, delimiters.size() - 1, generator)];
+      delimiters[generator.GetUInt64(delimiters.size() - 1u)];
 
-  MutationKind mutation_kind = static_cast<MutationKind>(GetRandomIntFromRange(
-      0, static_cast<size_t>(MutationKind::kNumMutationKinds) - 1, generator));
+  MutationKind mutation_kind = static_cast<MutationKind>(generator.GetUInt64(
+      static_cast<size_t>(MutationKind::kNumMutationKinds) - 1u));
 
   switch (mutation_kind) {
     case MutationKind::kSwapIntervals:
diff --git a/fuzzers/tint_regex_fuzzer/util.h b/fuzzers/tint_regex_fuzzer/util.h
deleted file mode 100644
index dfdc052..0000000
--- a/fuzzers/tint_regex_fuzzer/util.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2021 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     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 FUZZERS_TINT_REGEX_FUZZER_UTIL_H_
-#define FUZZERS_TINT_REGEX_FUZZER_UTIL_H_
-
-#include <random>
-
-namespace tint {
-namespace fuzzers {
-namespace regex_fuzzer {
-
-inline size_t GetRandomIntFromRange(size_t lower_bound,
-                                    size_t upper_bound,
-                                    std::mt19937& generator) {
-  std::uniform_int_distribution<size_t> dist(lower_bound, upper_bound);
-  return dist(generator);
-}
-}  // namespace regex_fuzzer
-}  // namespace fuzzers
-}  // namespace tint
-#endif  // FUZZERS_TINT_REGEX_FUZZER_UTIL_H_
diff --git a/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc b/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
index 2745001..164f0e4 100644
--- a/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
+++ b/fuzzers/tint_regex_fuzzer/wgsl_mutator.cc
@@ -17,13 +17,12 @@
 #include <cassert>
 #include <cstring>
 #include <map>
-#include <random>
 #include <regex>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "fuzzers/tint_regex_fuzzer/util.h"
+#include "fuzzers/random_generator.h"
 
 namespace tint {
 namespace fuzzers {
@@ -137,7 +136,7 @@
 
 bool SwapRandomIntervals(const std::string& delimiter,
                          std::string& wgsl_code,
-                         std::mt19937& generator) {
+                         RandomGenerator& generator) {
   std::vector<size_t> delimiter_positions =
       FindDelimiterIndices(delimiter, wgsl_code);
 
@@ -148,14 +147,10 @@
 
   // When generating the i-th random number, we should make sure that there are
   // at least (3-i) numbers greater than this number.
-  size_t ind1 =
-      GetRandomIntFromRange(0, delimiter_positions.size() - 3U, generator);
-  size_t ind2 = GetRandomIntFromRange(
-      ind1 + 1U, delimiter_positions.size() - 2U, generator);
-  size_t ind3 =
-      GetRandomIntFromRange(ind2, delimiter_positions.size() - 2U, generator);
-  size_t ind4 = GetRandomIntFromRange(
-      ind3 + 1U, delimiter_positions.size() - 1U, generator);
+  size_t ind1 = generator.GetUInt64(delimiter_positions.size() - 3u);
+  size_t ind2 = generator.GetUInt64(ind1 + 1u, delimiter_positions.size() - 2u);
+  size_t ind3 = generator.GetUInt64(ind2, delimiter_positions.size() - 2u);
+  size_t ind4 = generator.GetUInt64(ind3 + 1u, delimiter_positions.size() - 1u);
 
   SwapIntervals(delimiter_positions[ind1],
                 delimiter_positions[ind2] - delimiter_positions[ind1],
@@ -168,7 +163,7 @@
 
 bool DeleteRandomInterval(const std::string& delimiter,
                           std::string& wgsl_code,
-                          std::mt19937& generator) {
+                          RandomGenerator& generator) {
   std::vector<size_t> delimiter_positions =
       FindDelimiterIndices(delimiter, wgsl_code);
 
@@ -177,10 +172,8 @@
     return false;
   }
 
-  size_t ind1 =
-      GetRandomIntFromRange(0, delimiter_positions.size() - 2U, generator);
-  size_t ind2 = GetRandomIntFromRange(
-      ind1 + 1U, delimiter_positions.size() - 1U, generator);
+  size_t ind1 = generator.GetUInt64(delimiter_positions.size() - 2u);
+  size_t ind2 = generator.GetUInt64(ind1 + 1u, delimiter_positions.size() - 1u);
 
   DeleteInterval(delimiter_positions[ind1],
                  delimiter_positions[ind2] - delimiter_positions[ind1],
@@ -191,7 +184,7 @@
 
 bool DuplicateRandomInterval(const std::string& delimiter,
                              std::string& wgsl_code,
-                             std::mt19937& generator) {
+                             RandomGenerator& generator) {
   std::vector<size_t> delimiter_positions =
       FindDelimiterIndices(delimiter, wgsl_code);
 
@@ -200,13 +193,9 @@
     return false;
   }
 
-  size_t ind1 =
-      GetRandomIntFromRange(0, delimiter_positions.size() - 2U, generator);
-  size_t ind2 = GetRandomIntFromRange(
-      ind1 + 1U, delimiter_positions.size() - 1U, generator);
-
-  size_t ind3 =
-      GetRandomIntFromRange(0, delimiter_positions.size() - 1U, generator);
+  size_t ind1 = generator.GetUInt64(delimiter_positions.size() - 2u);
+  size_t ind2 = generator.GetUInt64(ind1 + 1u, delimiter_positions.size() - 1u);
+  size_t ind3 = generator.GetUInt64(delimiter_positions.size() - 1u);
 
   DuplicateInterval(delimiter_positions[ind1],
                     delimiter_positions[ind2] - delimiter_positions[ind1],
@@ -215,7 +204,8 @@
   return true;
 }
 
-bool ReplaceRandomIdentifier(std::string& wgsl_code, std::mt19937& generator) {
+bool ReplaceRandomIdentifier(std::string& wgsl_code,
+                             RandomGenerator& generator) {
   std::vector<std::pair<size_t, size_t>> identifiers =
       GetIdentifiers(wgsl_code);
 
@@ -224,15 +214,12 @@
     return false;
   }
 
-  size_t id1_index =
-      GetRandomIntFromRange(0, identifiers.size() - 1U, generator);
-
-  size_t id2_index =
-      GetRandomIntFromRange(0, identifiers.size() - 1U, generator);
+  size_t id1_index = generator.GetUInt64(identifiers.size() - 1u);
+  size_t id2_index = generator.GetUInt64(identifiers.size() - 1u);
 
   // The two identifiers must be different
   while (id1_index == id2_index) {
-    id2_index = GetRandomIntFromRange(0, identifiers.size() - 1U, generator);
+    id2_index = generator.GetUInt64(identifiers.size() - 1u);
   }
 
   ReplaceRegion(identifiers[id1_index].first, identifiers[id1_index].second,
@@ -242,7 +229,8 @@
   return true;
 }
 
-bool ReplaceRandomIntLiteral(std::string& wgsl_code, std::mt19937& generator) {
+bool ReplaceRandomIntLiteral(std::string& wgsl_code,
+                             RandomGenerator& generator) {
   std::vector<std::pair<size_t, size_t>> literals = GetIntLiterals(wgsl_code);
 
   // Need at least one integer literal
@@ -250,14 +238,13 @@
     return false;
   }
 
-  size_t id1_index = GetRandomIntFromRange(0, literals.size() - 1U, generator);
+  size_t id1_index = generator.GetUInt64(literals.size() - 1u);
 
   // INT_MAX = 2147483647, INT_MIN = -2147483648
   std::vector<std::string> boundary_values = {
       "2147483647", "-2147483648", "1", "-1", "0", "4294967295"};
 
-  size_t boundary_index =
-      GetRandomIntFromRange(0, boundary_values.size() - 1U, generator);
+  size_t boundary_index = generator.GetUInt64(boundary_values.size() - 1u);
 
   ReplaceInterval(literals[id1_index].first, literals[id1_index].second,
                   boundary_values[boundary_index], wgsl_code);
diff --git a/fuzzers/tint_regex_fuzzer/wgsl_mutator.h b/fuzzers/tint_regex_fuzzer/wgsl_mutator.h
index ba8d9d5..37ce688 100644
--- a/fuzzers/tint_regex_fuzzer/wgsl_mutator.h
+++ b/fuzzers/tint_regex_fuzzer/wgsl_mutator.h
@@ -15,11 +15,12 @@
 #ifndef FUZZERS_TINT_REGEX_FUZZER_WGSL_MUTATOR_H_
 #define FUZZERS_TINT_REGEX_FUZZER_WGSL_MUTATOR_H_
 
-#include <random>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "fuzzers/random_generator.h"
+
 namespace tint {
 namespace fuzzers {
 namespace regex_fuzzer {
@@ -114,7 +115,7 @@
 /// @return true if a swap happened or false otherwise.
 bool SwapRandomIntervals(const std::string& delimiter,
                          std::string& wgsl_code,
-                         std::mt19937& generator);
+                         RandomGenerator& generator);
 
 /// A function that, given a WGSL-like string and a delimiter,
 /// generates another WGSL-like string by deleting a random
@@ -125,7 +126,7 @@
 /// @return true if a deletion happened or false otherwise.
 bool DeleteRandomInterval(const std::string& delimiter,
                           std::string& wgsl_code,
-                          std::mt19937& generator);
+                          RandomGenerator& generator);
 
 /// A function that, given a WGSL-like string and a delimiter,
 /// generates another WGSL-like string by duplicating a random
@@ -136,20 +137,22 @@
 /// @return true if a duplication happened or false otherwise.
 bool DuplicateRandomInterval(const std::string& delimiter,
                              std::string& wgsl_code,
-                             std::mt19937& generator);
+                             RandomGenerator& generator);
 
 /// Replaces a randomly-chosen identifier in wgsl_code.
 /// @param wgsl_code - WGSL-like string where the replacement will occur.
 /// @param generator - the random number generator.
 /// @return true if a replacement happened or false otherwise.
-bool ReplaceRandomIdentifier(std::string& wgsl_code, std::mt19937& generator);
+bool ReplaceRandomIdentifier(std::string& wgsl_code,
+                             RandomGenerator& generator);
 
 /// Replaces the value of a randomly-chosen integer with one of
 /// the values in the set {INT_MAX, INT_MIN, 0, -1}.
 /// @param wgsl_code - WGSL-like string where the replacement will occur.
 /// @param generator - the random number generator.
 /// @return true if a replacement happened or false otherwise.
-bool ReplaceRandomIntLiteral(std::string& wgsl_code, std::mt19937& generator);
+bool ReplaceRandomIntLiteral(std::string& wgsl_code,
+                             RandomGenerator& generator);
 
 }  // namespace regex_fuzzer
 }  // namespace fuzzers
diff --git a/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt b/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt
index 92790c1..6bb2097 100644
--- a/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt
+++ b/fuzzers/tint_spirv_tools_fuzzer/CMakeLists.txt
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 set(FUZZER_SOURCES
+        ../random_generator.cc
         cli.cc
         fuzzer.cc
         mutator.cc
@@ -23,6 +24,7 @@
         util.cc)
 
 set(FUZZER_SOURCES ${FUZZER_SOURCES}
+        ../random_generator.h
         cli.h
         mutator.h
         mutator_cache.h
@@ -32,8 +34,8 @@
         util.h)
 
 set(FUZZER_SOURCES ${FUZZER_SOURCES}
-        ../tint_common_fuzzer.h
-        ../tint_common_fuzzer.cc)
+        ../tint_common_fuzzer.cc
+        ../tint_common_fuzzer.h)
 
 function(configure_spirv_tools_fuzzer_target NAME SOURCES)
     add_executable(${NAME} ${SOURCES})
@@ -61,6 +63,7 @@
 target_link_libraries(tint_spirv_tools_fuzzer libtint-fuzz)
 
 set(DEBUGGER_SOURCES
+        ../random_generator.cc
         cli.cc
         mutator.cc
         mutator_debugger.cc
@@ -70,6 +73,7 @@
         util.cc)
 
 set(DEBUGGER_SOURCES ${DEBUGGER_SOURCES}
+        ../random_generator.h
         cli.h
         mutator.h
         spirv_fuzz_mutator.h
diff --git a/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc b/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc
index ce6fec5..0ffc9a2 100644
--- a/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc
+++ b/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc
@@ -14,10 +14,10 @@
 
 #include <cassert>
 #include <memory>
-#include <random>
 #include <string>
 #include <vector>
 
+#include "fuzzers/random_generator.h"
 #include "fuzzers/tint_common_fuzzer.h"
 #include "fuzzers/tint_spirv_tools_fuzzer/cli.h"
 #include "fuzzers/tint_spirv_tools_fuzzer/mutator_cache.h"
@@ -67,9 +67,8 @@
   }
 
   assert(!types.empty() && "At least one mutator type must be specified");
-  std::mt19937 rng(seed);
-  auto mutator_type =
-      types[std::uniform_int_distribution<size_t>(0, types.size() - 1)(rng)];
+  RandomGenerator generator(seed);
+  auto mutator_type = types[generator.GetUInt64(types.size())];
 
   const auto& mutator_params = context->params.mutator_params;
   switch (mutator_type) {
diff --git a/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.cc b/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.cc
index 360f1d9..0b75ed3 100644
--- a/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.cc
+++ b/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.cc
@@ -66,7 +66,7 @@
       optimized_binary_(),
       validate_after_each_opt_(validate_after_each_opt),
       opt_batch_size_(opt_batch_size),
-      rng_(seed) {
+      generator_(seed) {
   assert(spvtools::SpirvTools(target_env).Validate(original_binary_) &&
          "Initial binary is invalid");
   assert(!opt_passes_.empty() && "Must be at least one pass");
@@ -105,8 +105,7 @@
     std::vector<std::string> passes;
 
     while (passes.size() < num_of_passes) {
-      auto idx = std::uniform_int_distribution<size_t>(
-          0, opt_passes_.size() - 1)(rng_);
+      auto idx = generator_.GetUInt64(opt_passes_.size());
       passes.push_back(opt_passes_[idx]);
     }
 
diff --git a/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.h b/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.h
index 6d055e4..51514fa 100644
--- a/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.h
+++ b/fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.h
@@ -15,11 +15,11 @@
 #ifndef FUZZERS_TINT_SPIRV_TOOLS_FUZZER_SPIRV_OPT_MUTATOR_H_
 #define FUZZERS_TINT_SPIRV_TOOLS_FUZZER_SPIRV_OPT_MUTATOR_H_
 
-#include <random>
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "fuzzers/random_generator.h"
 #include "fuzzers/tint_spirv_tools_fuzzer/mutator.h"
 #include "spirv-tools/libspirv.h"
 
@@ -86,7 +86,7 @@
   std::stringstream errors_;
 
   // The random number generator initialized with `seed_`.
-  std::mt19937 rng_;
+  RandomGenerator generator_;
 };
 
 }  // namespace spvtools_fuzzer
diff --git a/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.cc b/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.cc
index dccbfe3..e95a8de 100644
--- a/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.cc
+++ b/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.cc
@@ -44,7 +44,7 @@
                                        bool validate_after_each_reduction)
     : ir_context_(nullptr),
       finders_(),
-      rng_(seed),
+      generator_(seed),
       errors_(),
       is_valid_(true),
       reductions_batch_size_(reductions_batch_size),
diff --git a/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.h b/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.h
index 4d656d8..2f25146 100644
--- a/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.h
+++ b/fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.h
@@ -16,12 +16,12 @@
 #define FUZZERS_TINT_SPIRV_TOOLS_FUZZER_SPIRV_REDUCE_MUTATOR_H_
 
 #include <memory>
-#include <random>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "fuzzers/random_generator.h"
 #include "fuzzers/tint_spirv_tools_fuzzer/mutator.h"
 
 #include "source/reduce/reduction_opportunity_finder.h"
@@ -65,7 +65,7 @@
  private:
   template <typename T, typename... Args>
   void MaybeAddFinder(Args&&... args) {
-    if (enable_all_reductions_ || std::uniform_int_distribution<>(0, 1)(rng_)) {
+    if (enable_all_reductions_ || generator_.GetBool()) {
       finders_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
     }
   }
@@ -73,16 +73,14 @@
   template <typename T>
   T* GetRandomElement(std::vector<T>* arr) {
     assert(!arr->empty() && "Can't get random element from an empty vector");
-    auto index =
-        std::uniform_int_distribution<size_t>(0, arr->size() - 1)(rng_);
+    auto index = generator_.GetUInt64(arr->size());
     return &(*arr)[index];
   }
 
   template <typename T>
   T* GetRandomElement(std::vector<std::unique_ptr<T>>* arr) {
     assert(!arr->empty() && "Can't get random element from an empty vector");
-    auto index =
-        std::uniform_int_distribution<size_t>(0, arr->size() - 1)(rng_);
+    auto index = generator_.GetUInt64(arr->size());
     return (*arr)[index].get();
   }
 
@@ -97,7 +95,7 @@
       finders_;
 
   // Random number generator initialized with `seed_`.
-  std::mt19937 rng_;
+  RandomGenerator generator_;
 
   // All the errors produced by the reducer.
   std::stringstream errors_;