Remove ceres/internal/random.h in favor of <random>

Fixes https://github.com/ceres-solver/ceres-solver/issues/854

Change-Id: Id30b8dc2221f9afe4eb83f3a9304b9b2bc7e05d4
diff --git a/examples/BUILD b/examples/BUILD
index 7bacf22..00b799e 100644
--- a/examples/BUILD
+++ b/examples/BUILD
@@ -47,7 +47,6 @@
         "bal_problem.cc",
         "bal_problem.h",
         "bundle_adjuster.cc",
-        "random.h",
         "snavely_reprojection_error.h",
     ],
     copts = EXAMPLE_COPTS,
@@ -69,7 +68,6 @@
 cc_binary(
     name = "robot_pose_mle",
     srcs = [
-        "random.h",
         "robot_pose_mle.cc",
     ],
     copts = EXAMPLE_COPTS,
diff --git a/examples/bal_problem.cc b/examples/bal_problem.cc
index 99bc075..2423fb6 100644
--- a/examples/bal_problem.cc
+++ b/examples/bal_problem.cc
@@ -32,15 +32,15 @@
 
 #include <algorithm>
 #include <cstdio>
-#include <cstdlib>
 #include <fstream>
+#include <functional>
+#include <random>
 #include <string>
 #include <vector>
 
 #include "Eigen/Core"
 #include "ceres/rotation.h"
 #include "glog/logging.h"
-#include "random.h"
 
 namespace ceres::examples {
 namespace {
@@ -55,9 +55,9 @@
   }
 }
 
-void PerturbPoint3(const double sigma, double* point) {
+void PerturbPoint3(std::function<double()> dist, double* point) {
   for (int i = 0; i < 3; ++i) {
-    point[i] += RandNormal() * sigma;
+    point[i] += dist();
   }
 }
 
@@ -297,14 +297,19 @@
   CHECK_GE(point_sigma, 0.0);
   CHECK_GE(rotation_sigma, 0.0);
   CHECK_GE(translation_sigma, 0.0);
-
+  std::mt19937 prng;
+  std::normal_distribution point_noise_distribution(0.0, point_sigma);
   double* points = mutable_points();
   if (point_sigma > 0) {
     for (int i = 0; i < num_points_; ++i) {
-      PerturbPoint3(point_sigma, points + 3 * i);
+      PerturbPoint3(std::bind(point_noise_distribution, std::ref(prng)),
+                    points + 3 * i);
     }
   }
 
+  std::normal_distribution rotation_noise_distribution(0.0, point_sigma);
+  std::normal_distribution translation_noise_distribution(0.0,
+                                                          translation_sigma);
   for (int i = 0; i < num_cameras_; ++i) {
     double* camera = mutable_cameras() + camera_block_size() * i;
 
@@ -314,12 +319,14 @@
     // representation.
     CameraToAngleAxisAndCenter(camera, angle_axis, center);
     if (rotation_sigma > 0.0) {
-      PerturbPoint3(rotation_sigma, angle_axis);
+      PerturbPoint3(std::bind(rotation_noise_distribution, std::ref(prng)),
+                    angle_axis);
     }
     AngleAxisAndCenterToCamera(angle_axis, center, camera);
 
     if (translation_sigma > 0.0) {
-      PerturbPoint3(translation_sigma, camera + camera_block_size() - 6);
+      PerturbPoint3(std::bind(translation_noise_distribution, std::ref(prng)),
+                    camera + camera_block_size() - 6);
     }
   }
 }
diff --git a/examples/robot_pose_mle.cc b/examples/robot_pose_mle.cc
index c199b89..f924bc3 100644
--- a/examples/robot_pose_mle.cc
+++ b/examples/robot_pose_mle.cc
@@ -127,13 +127,13 @@
 
 #include <cmath>
 #include <cstdio>
+#include <random>
 #include <vector>
 
 #include "ceres/ceres.h"
 #include "ceres/dynamic_autodiff_cost_function.h"
 #include "gflags/gflags.h"
 #include "glog/logging.h"
-#include "random.h"
 
 using ceres::AutoDiffCostFunction;
 using ceres::CauchyLoss;
@@ -143,7 +143,6 @@
 using ceres::Problem;
 using ceres::Solve;
 using ceres::Solver;
-using ceres::examples::RandNormal;
 using std::min;
 using std::vector;
 
@@ -247,6 +246,10 @@
   const int num_steps =
       static_cast<int>(ceil(CERES_GET_FLAG(FLAGS_corridor_length) /
                             CERES_GET_FLAG(FLAGS_pose_separation)));
+  std::mt19937 prng;
+  std::normal_distribution odometry_noise(
+      0.0, CERES_GET_FLAG(FLAGS_odometry_stddev));
+  std::normal_distribution range_noise(0.0, CERES_GET_FLAG(FLAGS_range_stddev));
 
   // The robot starts out at the origin.
   double robot_location = 0.0;
@@ -258,10 +261,8 @@
     const double actual_range =
         CERES_GET_FLAG(FLAGS_corridor_length) - robot_location;
     const double observed_odometry =
-        RandNormal() * CERES_GET_FLAG(FLAGS_odometry_stddev) +
-        actual_odometry_value;
-    const double observed_range =
-        RandNormal() * CERES_GET_FLAG(FLAGS_range_stddev) + actual_range;
+        actual_odometry_value + odometry_noise(prng);
+    const double observed_range = actual_range + range_noise(prng);
     odometry_values->push_back(observed_odometry);
     range_readings->push_back(observed_range);
   }
diff --git a/internal/ceres/autodiff_test.cc b/internal/ceres/autodiff_test.cc
index 5f769a0..629eea8 100644
--- a/internal/ceres/autodiff_test.cc
+++ b/internal/ceres/autodiff_test.cc
@@ -30,7 +30,8 @@
 
 #include "ceres/internal/autodiff.h"
 
-#include "ceres/random.h"
+#include <random>
+
 #include "gtest/gtest.h"
 
 namespace ceres::internal {
@@ -162,17 +163,18 @@
 
 // Test projective camera model projector.
 TEST(AutoDiff, ProjectiveCameraModel) {
-  srand(5);
   double const tol = 1e-10;  // floating-point tolerance.
   double const del = 1e-4;   // finite-difference step.
   double const err = 1e-6;   // finite-difference tolerance.
 
   Projective b;
+  std::mt19937 prng;
+  std::uniform_real_distribution<double> uniform01(0.0, 1.0);
 
   // Make random P and X, in a single vector.
   double PX[12 + 4];
   for (double& PX_i : PX) {
-    PX_i = RandDouble();
+    PX_i = uniform01(prng);
   }
 
   // Handy names for the P and X parts.
@@ -282,16 +284,17 @@
 
 // This test is similar in structure to the previous one.
 TEST(AutoDiff, Metric) {
-  srand(5);
   double const tol = 1e-10;  // floating-point tolerance.
   double const del = 1e-4;   // finite-difference step.
-  double const err = 1e-5;   // finite-difference tolerance.
+  double const err = 2e-5;   // finite-difference tolerance.
 
   Metric b;
 
   // Make random parameter vector.
   double qcX[4 + 3 + 3];
-  for (double& qcX_i : qcX) qcX_i = RandDouble();
+  std::mt19937 prng;
+  std::uniform_real_distribution<double> uniform01(0.0, 1.0);
+  for (double& qcX_i : qcX) qcX_i = uniform01(prng);
 
   // Handy names.
   double* q = qcX;
diff --git a/internal/ceres/block_sparse_matrix.cc b/internal/ceres/block_sparse_matrix.cc
index 22224a3..9ae4384 100644
--- a/internal/ceres/block_sparse_matrix.cc
+++ b/internal/ceres/block_sparse_matrix.cc
@@ -33,12 +33,12 @@
 #include <algorithm>
 #include <cstddef>
 #include <memory>
+#include <random>
 #include <vector>
 
 #include "ceres/block_structure.h"
 #include "ceres/crs_matrix.h"
 #include "ceres/internal/eigen.h"
-#include "ceres/random.h"
 #include "ceres/small_blas.h"
 #include "ceres/triplet_sparse_matrix.h"
 #include "glog/logging.h"
@@ -382,6 +382,11 @@
   CHECK_GT(options.block_density, 0.0);
   CHECK_LE(options.block_density, 1.0);
 
+  std::mt19937 prng;
+  std::uniform_int_distribution col_distribution(options.min_col_block_size,
+                                                 options.max_col_block_size);
+  std::uniform_int_distribution row_distribution(options.min_row_block_size,
+                                                 options.max_row_block_size);
   auto* bs = new CompressedRowBlockStructure();
   if (options.col_blocks.empty()) {
     CHECK_GT(options.num_col_blocks, 0);
@@ -392,10 +397,7 @@
     // Generate the col block structure.
     int col_block_position = 0;
     for (int i = 0; i < options.num_col_blocks; ++i) {
-      // Generate a random integer in [min_col_block_size, max_col_block_size]
-      const int delta_block_size =
-          Uniform(options.max_col_block_size - options.min_col_block_size);
-      const int col_block_size = options.min_col_block_size + delta_block_size;
+      const int col_block_size = col_distribution(prng);
       bs->cols.emplace_back(col_block_size, col_block_position);
       col_block_position += col_block_size;
     }
@@ -404,22 +406,21 @@
   }
 
   bool matrix_has_blocks = false;
+  std::uniform_real_distribution uniform01(0.0, 1.0);
   while (!matrix_has_blocks) {
     VLOG(1) << "Clearing";
     bs->rows.clear();
     int row_block_position = 0;
     int value_position = 0;
     for (int r = 0; r < options.num_row_blocks; ++r) {
-      const int delta_block_size =
-          Uniform(options.max_row_block_size - options.min_row_block_size);
-      const int row_block_size = options.min_row_block_size + delta_block_size;
+      const int row_block_size = row_distribution(prng);
       bs->rows.emplace_back();
       CompressedRow& row = bs->rows.back();
       row.block.size = row_block_size;
       row.block.position = row_block_position;
       row_block_position += row_block_size;
       for (int c = 0; c < bs->cols.size(); ++c) {
-        if (RandDouble() > options.block_density) continue;
+        if (uniform01(prng) > options.block_density) continue;
 
         row.cells.emplace_back();
         Cell& cell = row.cells.back();
@@ -433,8 +434,9 @@
 
   auto matrix = std::make_unique<BlockSparseMatrix>(bs);
   double* values = matrix->mutable_values();
+  std::normal_distribution standard_normal_distribution;
   for (int i = 0; i < matrix->num_nonzeros(); ++i) {
-    values[i] = RandNormal();
+    values[i] = standard_normal_distribution(prng);
   }
 
   return matrix;
diff --git a/internal/ceres/compressed_row_sparse_matrix.cc b/internal/ceres/compressed_row_sparse_matrix.cc
index d663f4a..bd94fdc 100644
--- a/internal/ceres/compressed_row_sparse_matrix.cc
+++ b/internal/ceres/compressed_row_sparse_matrix.cc
@@ -31,13 +31,14 @@
 #include "ceres/compressed_row_sparse_matrix.h"
 
 #include <algorithm>
+#include <functional>
 #include <memory>
 #include <numeric>
+#include <random>
 #include <vector>
 
 #include "ceres/crs_matrix.h"
 #include "ceres/internal/export.h"
-#include "ceres/random.h"
 #include "ceres/triplet_sparse_matrix.h"
 #include "glog/logging.h"
 
@@ -122,6 +123,7 @@
                     const int num_cols,
                     const int row_block_begin,
                     const int col_block_begin,
+                    std::function<double()> dist,
                     std::vector<int>* rows,
                     std::vector<int>* cols,
                     std::vector<double>* values) {
@@ -129,19 +131,20 @@
     for (int c = 0; c < num_cols; ++c) {
       rows->push_back(row_block_begin + r);
       cols->push_back(col_block_begin + c);
-      values->push_back(RandNormal());
+      values->push_back(dist());
     }
   }
 }
 
 void AddSymmetricRandomBlock(const int num_rows,
                              const int row_block_begin,
+                             std::function<double()> dist,
                              std::vector<int>* rows,
                              std::vector<int>* cols,
                              std::vector<double>* values) {
   for (int r = 0; r < num_rows; ++r) {
     for (int c = r; c < num_rows; ++c) {
-      const double v = RandNormal();
+      const double v = dist();
       rows->push_back(row_block_begin + r);
       cols->push_back(row_block_begin + c);
       values->push_back(v);
@@ -644,21 +647,26 @@
   vector<int> row_blocks;
   vector<int> col_blocks;
 
+  std::mt19937 prng;
+  std::uniform_int_distribution col_distribution(options.min_col_block_size,
+                                                 options.max_col_block_size);
+  std::uniform_int_distribution row_distribution(options.min_row_block_size,
+                                                 options.max_row_block_size);
+  std::uniform_real_distribution uniform01(0.0, 1.0);
+  std::normal_distribution standard_normal_distribution;
+  auto values_dist = std::bind(standard_normal_distribution, std::ref(prng));
+
   // Generate the row block structure.
   for (int i = 0; i < options.num_row_blocks; ++i) {
     // Generate a random integer in [min_row_block_size, max_row_block_size]
-    const int delta_block_size =
-        Uniform(options.max_row_block_size - options.min_row_block_size);
-    row_blocks.push_back(options.min_row_block_size + delta_block_size);
+    row_blocks.push_back(row_distribution(prng));
   }
 
   if (options.storage_type == StorageType::UNSYMMETRIC) {
     // Generate the col block structure.
     for (int i = 0; i < options.num_col_blocks; ++i) {
       // Generate a random integer in [min_col_block_size, max_col_block_size]
-      const int delta_block_size =
-          Uniform(options.max_col_block_size - options.min_col_block_size);
-      col_blocks.push_back(options.min_col_block_size + delta_block_size);
+      col_blocks.push_back(col_distribution(prng));
     }
   } else {
     // Symmetric matrices (LOWER_TRIANGULAR or UPPER_TRIANGULAR);
@@ -695,7 +703,7 @@
         }
 
         // Randomly determine if this block is present or not.
-        if (RandDouble() <= options.block_density) {
+        if (uniform01(prng) <= options.block_density) {
           // If the matrix is symmetric, then we take care to generate
           // symmetric diagonal blocks.
           if (options.storage_type == StorageType::UNSYMMETRIC || r != c) {
@@ -703,12 +711,14 @@
                            col_blocks[c],
                            row_block_begin,
                            col_block_begin,
+                           values_dist,
                            &tsm_rows,
                            &tsm_cols,
                            &tsm_values);
           } else {
             AddSymmetricRandomBlock(row_blocks[r],
                                     row_block_begin,
+                                    values_dist,
                                     &tsm_rows,
                                     &tsm_cols,
                                     &tsm_values);
diff --git a/internal/ceres/compressed_row_sparse_matrix_test.cc b/internal/ceres/compressed_row_sparse_matrix_test.cc
index 4151845..db90d8b 100644
--- a/internal/ceres/compressed_row_sparse_matrix_test.cc
+++ b/internal/ceres/compressed_row_sparse_matrix_test.cc
@@ -33,6 +33,7 @@
 #include <algorithm>
 #include <memory>
 #include <numeric>
+#include <random>
 #include <string>
 
 #include "Eigen/SparseCore"
@@ -40,7 +41,6 @@
 #include "ceres/crs_matrix.h"
 #include "ceres/internal/eigen.h"
 #include "ceres/linear_least_squares_problems.h"
-#include "ceres/random.h"
 #include "ceres/triplet_sparse_matrix.h"
 #include "glog/logging.h"
 #include "gtest/gtest.h"
@@ -404,7 +404,8 @@
   const int kMinBlockSize = 1;
   const int kMaxBlockSize = 5;
   const int kNumTrials = 10;
-
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform(0.5, 1.0);
   for (int num_blocks = kMinNumBlocks; num_blocks < kMaxNumBlocks;
        ++num_blocks) {
     for (int trial = 0; trial < kNumTrials; ++trial) {
@@ -416,7 +417,7 @@
       options.num_row_blocks = 2 * num_blocks;
       options.min_row_block_size = kMinBlockSize;
       options.max_row_block_size = kMaxBlockSize;
-      options.block_density = std::max(0.5, RandDouble());
+      options.block_density = uniform(prng);
       options.storage_type = ::testing::get<0>(param);
       std::unique_ptr<CompressedRowSparseMatrix> matrix =
           CompressedRowSparseMatrix::CreateRandomMatrix(options);
@@ -473,7 +474,8 @@
   const int kMinBlockSize = 1;
   const int kMaxBlockSize = 5;
   const int kNumTrials = 10;
-
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform(0.5, 1.0);
   for (int num_blocks = kMinNumBlocks; num_blocks < kMaxNumBlocks;
        ++num_blocks) {
     for (int trial = 0; trial < kNumTrials; ++trial) {
@@ -485,7 +487,7 @@
       options.num_row_blocks = 2 * num_blocks;
       options.min_row_block_size = kMinBlockSize;
       options.max_row_block_size = kMaxBlockSize;
-      options.block_density = std::max(0.5, RandDouble());
+      options.block_density = uniform(prng);
       options.storage_type = ::testing::get<0>(param);
       std::unique_ptr<CompressedRowSparseMatrix> matrix =
           CompressedRowSparseMatrix::CreateRandomMatrix(options);
@@ -542,7 +544,8 @@
   const int kMinBlockSize = 1;
   const int kMaxBlockSize = 5;
   const int kNumTrials = 10;
-
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform(0.5, 1.0);
   for (int num_blocks = kMinNumBlocks; num_blocks < kMaxNumBlocks;
        ++num_blocks) {
     for (int trial = 0; trial < kNumTrials; ++trial) {
@@ -554,7 +557,7 @@
       options.num_row_blocks = 2 * num_blocks;
       options.min_row_block_size = kMinBlockSize;
       options.max_row_block_size = kMaxBlockSize;
-      options.block_density = std::max(0.5, RandDouble());
+      options.block_density = uniform(prng);
       options.storage_type = ::testing::get<0>(param);
       std::unique_ptr<CompressedRowSparseMatrix> matrix =
           CompressedRowSparseMatrix::CreateRandomMatrix(options);
diff --git a/internal/ceres/corrector_test.cc b/internal/ceres/corrector_test.cc
index d2f7c84..b1e9bee 100644
--- a/internal/ceres/corrector_test.cc
+++ b/internal/ceres/corrector_test.cc
@@ -34,9 +34,9 @@
 #include <cmath>
 #include <cstdlib>
 #include <cstring>
+#include <random>
 
 #include "ceres/internal/eigen.h"
-#include "ceres/random.h"
 #include "gtest/gtest.h"
 
 namespace ceres {
@@ -160,18 +160,19 @@
   // and hessians.
   Matrix c_hess(2, 2);
   Vector c_grad(2);
-
-  srand(5);
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform01(0.0, 1.0);
   for (int iter = 0; iter < 10000; ++iter) {
     // Initialize the jacobian and residual.
-    for (double& jacobian_entry : jacobian) jacobian_entry = RandDouble();
-    for (double& residual : residuals) residual = RandDouble();
+    for (double& jacobian_entry : jacobian) jacobian_entry = uniform01(prng);
+    for (double& residual : residuals) residual = uniform01(prng);
 
     const double sq_norm = res.dot(res);
 
     rho[0] = sq_norm;
-    rho[1] = RandDouble();
-    rho[2] = 2.0 * RandDouble() - 1.0;
+    rho[1] = uniform01(prng);
+    rho[2] = uniform01(
+        prng, std::uniform_real_distribution<double>::param_type(-1, 1));
 
     // If rho[2] > 0, then the curvature correction to the correction
     // and the gauss newton approximation will match. Otherwise, we
@@ -227,10 +228,11 @@
   Matrix c_hess(2, 2);
   Vector c_grad(2);
 
-  srand(5);
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform01(0.0, 1.0);
   for (int iter = 0; iter < 10000; ++iter) {
     // Initialize the jacobian.
-    for (double& jacobian_entry : jacobian) jacobian_entry = RandDouble();
+    for (double& jacobian_entry : jacobian) jacobian_entry = uniform01(prng);
 
     // Zero residuals
     res.setZero();
@@ -238,8 +240,9 @@
     const double sq_norm = res.dot(res);
 
     rho[0] = sq_norm;
-    rho[1] = RandDouble();
-    rho[2] = 2 * RandDouble() - 1.0;
+    rho[1] = uniform01(prng);
+    rho[2] = uniform01(
+        prng, std::uniform_real_distribution<double>::param_type(-1, 1));
 
     // Ground truth values.
     g_res = sqrt(rho[1]) * res;
diff --git a/internal/ceres/dense_linear_solver_benchmark.cc b/internal/ceres/dense_linear_solver_benchmark.cc
index 53628ad..a01e0d6 100644
--- a/internal/ceres/dense_linear_solver_benchmark.cc
+++ b/internal/ceres/dense_linear_solver_benchmark.cc
@@ -34,7 +34,6 @@
 #include "ceres/dense_sparse_matrix.h"
 #include "ceres/internal/config.h"
 #include "ceres/linear_solver.h"
-#include "ceres/random.h"
 
 namespace ceres::internal {
 
diff --git a/internal/ceres/gradient_checker_test.cc b/internal/ceres/gradient_checker_test.cc
index aedb0b9..5de4778 100644
--- a/internal/ceres/gradient_checker_test.cc
+++ b/internal/ceres/gradient_checker_test.cc
@@ -34,12 +34,12 @@
 
 #include <cmath>
 #include <cstdlib>
+#include <random>
 #include <utility>
 #include <vector>
 
 #include "ceres/cost_function.h"
 #include "ceres/problem.h"
-#include "ceres/random.h"
 #include "ceres/solver.h"
 #include "ceres/test_util.h"
 #include "glog/logging.h"
@@ -60,12 +60,15 @@
 class GoodTestTerm : public CostFunction {
  public:
   GoodTestTerm(int arity, int const* dim) : arity_(arity), return_value_(true) {
+    std::mt19937 prng;
+    std::uniform_real_distribution distribution(-1.0, 1.0);
+
     // Make 'arity' random vectors.
     a_.resize(arity_);
     for (int j = 0; j < arity_; ++j) {
       a_[j].resize(dim[j]);
       for (int u = 0; u < dim[j]; ++u) {
-        a_[j][u] = 2.0 * RandDouble() - 1.0;
+        a_[j][u] = distribution(prng);
       }
     }
 
@@ -119,12 +122,14 @@
 class BadTestTerm : public CostFunction {
  public:
   BadTestTerm(int arity, int const* dim) : arity_(arity) {
+    std::mt19937 prng;
+    std::uniform_real_distribution distribution(-1.0, 1.0);
     // Make 'arity' random vectors.
     a_.resize(arity_);
     for (int j = 0; j < arity_; ++j) {
       a_[j].resize(dim[j]);
       for (int u = 0; u < dim[j]; ++u) {
-        a_[j][u] = 2.0 * RandDouble() - 1.0;
+        a_[j][u] = distribution(prng);
       }
     }
 
@@ -194,8 +199,6 @@
 }
 
 TEST(GradientChecker, SmokeTest) {
-  srand(5);
-
   // Test with 3 blocks of size 2, 3 and 4.
   int const num_parameters = 3;
   std::vector<int> parameter_sizes(3);
@@ -205,10 +208,12 @@
 
   // Make a random set of blocks.
   FixedArray<double*> parameters(num_parameters);
+  std::mt19937 prng;
+  std::uniform_real_distribution distribution(-1.0, 1.0);
   for (int j = 0; j < num_parameters; ++j) {
     parameters[j] = new double[parameter_sizes[j]];
     for (int u = 0; u < parameter_sizes[j]; ++u) {
-      parameters[j][u] = 2.0 * RandDouble() - 1.0;
+      parameters[j][u] = distribution(prng);
     }
   }
 
diff --git a/internal/ceres/gradient_checking_cost_function_test.cc b/internal/ceres/gradient_checking_cost_function_test.cc
index 5c6046f..99e0727 100644
--- a/internal/ceres/gradient_checking_cost_function_test.cc
+++ b/internal/ceres/gradient_checking_cost_function_test.cc
@@ -33,6 +33,7 @@
 #include <cmath>
 #include <cstdint>
 #include <memory>
+#include <random>
 #include <vector>
 
 #include "ceres/cost_function.h"
@@ -41,7 +42,6 @@
 #include "ceres/parameter_block.h"
 #include "ceres/problem_impl.h"
 #include "ceres/program.h"
-#include "ceres/random.h"
 #include "ceres/residual_block.h"
 #include "ceres/sized_cost_function.h"
 #include "ceres/types.h"
@@ -70,12 +70,14 @@
   // The constructor of this function needs to know the number
   // of blocks desired, and the size of each block.
   TestTerm(int arity, int const* dim) : arity_(arity) {
+    std::mt19937 prng;
+    std::uniform_real_distribution uniform01(-1.0, 1.0);
     // Make 'arity' random vectors.
     a_.resize(arity_);
     for (int j = 0; j < arity_; ++j) {
       a_[j].resize(dim[j]);
       for (int u = 0; u < dim[j]; ++u) {
-        a_[j][u] = 2.0 * RandDouble() - 1.0;
+        a_[j][u] = uniform01(prng);
       }
     }
 
@@ -130,18 +132,18 @@
 };
 
 TEST(GradientCheckingCostFunction, ResidualsAndJacobiansArePreservedTest) {
-  srand(5);
-
   // Test with 3 blocks of size 2, 3 and 4.
   int const arity = 3;
   int const dim[arity] = {2, 3, 4};
 
   // Make a random set of blocks.
   vector<double*> parameters(arity);
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform01(-1.0, 1.0);
   for (int j = 0; j < arity; ++j) {
     parameters[j] = new double[dim[j]];
     for (int u = 0; u < dim[j]; ++u) {
-      parameters[j][u] = 2.0 * RandDouble() - 1.0;
+      parameters[j][u] = uniform01(prng);
     }
   }
 
@@ -187,18 +189,18 @@
 }
 
 TEST(GradientCheckingCostFunction, SmokeTest) {
-  srand(5);
-
   // Test with 3 blocks of size 2, 3 and 4.
   int const arity = 3;
   int const dim[arity] = {2, 3, 4};
 
   // Make a random set of blocks.
   vector<double*> parameters(arity);
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform01(-1.0, 1.0);
   for (int j = 0; j < arity; ++j) {
     parameters[j] = new double[dim[j]];
     for (int u = 0; u < dim[j]; ++u) {
-      parameters[j][u] = 2.0 * RandDouble() - 1.0;
+      parameters[j][u] = uniform01(prng);
     }
   }
 
diff --git a/internal/ceres/inner_product_computer_test.cc b/internal/ceres/inner_product_computer_test.cc
index a1f5c49..9c0382b 100644
--- a/internal/ceres/inner_product_computer_test.cc
+++ b/internal/ceres/inner_product_computer_test.cc
@@ -32,11 +32,11 @@
 
 #include <memory>
 #include <numeric>
+#include <random>
 
 #include "Eigen/SparseCore"
 #include "ceres/block_sparse_matrix.h"
 #include "ceres/internal/eigen.h"
-#include "ceres/random.h"
 #include "ceres/triplet_sparse_matrix.h"
 #include "glog/logging.h"
 #include "gtest/gtest.h"
@@ -77,11 +77,11 @@
   }
 
 TEST(InnerProductComputer, NormalOperation) {
-  // "Randomly generated seed."
-  SetRandomState(29823);
   const int kMaxNumRowBlocks = 10;
   const int kMaxNumColBlocks = 10;
   const int kNumTrials = 10;
+  std::mt19937 prng;
+  std::uniform_real_distribution distribution(0.01, 1.0);
 
   // Create a random matrix, compute its outer product using Eigen and
   // ComputeOuterProduct. Convert both matrices to dense matrices and
@@ -98,7 +98,8 @@
         options.max_row_block_size = 5;
         options.min_col_block_size = 1;
         options.max_col_block_size = 10;
-        options.block_density = std::max(0.1, RandDouble());
+        options.block_density = distribution(prng);
+
         VLOG(2) << "num row blocks: " << options.num_row_blocks;
         VLOG(2) << "num col blocks: " << options.num_col_blocks;
         VLOG(2) << "min row block size: " << options.min_row_block_size;
@@ -143,6 +144,8 @@
   const int kNumRowBlocks = 10;
   const int kNumColBlocks = 20;
   const int kNumTrials = 5;
+  std::mt19937 prng;
+  std::uniform_real_distribution distribution(0.01, 1.0);
 
   // Create a random matrix, compute its outer product using Eigen and
   // ComputeInnerProductComputer. Convert both matrices to dense matrices and
@@ -155,7 +158,7 @@
     options.max_row_block_size = 5;
     options.min_col_block_size = 1;
     options.max_col_block_size = 10;
-    options.block_density = std::min(0.01, RandDouble());
+    options.block_density = distribution(prng);
 
     VLOG(2) << "num row blocks: " << options.num_row_blocks;
     VLOG(2) << "num col blocks: " << options.num_col_blocks;
diff --git a/internal/ceres/normal_prior_test.cc b/internal/ceres/normal_prior_test.cc
index c66f9d6..9c84555 100644
--- a/internal/ceres/normal_prior_test.cc
+++ b/internal/ceres/normal_prior_test.cc
@@ -31,43 +31,26 @@
 #include "ceres/normal_prior.h"
 
 #include <cstddef>
+#include <random>
 
 #include "ceres/internal/eigen.h"
-#include "ceres/random.h"
 #include "gtest/gtest.h"
 
 namespace ceres {
 namespace internal {
 
-namespace {
-
-void RandomVector(Vector* v) {
-  for (int r = 0; r < v->rows(); ++r) (*v)[r] = 2 * RandDouble() - 1;
-}
-
-void RandomMatrix(Matrix* m) {
-  for (int r = 0; r < m->rows(); ++r) {
-    for (int c = 0; c < m->cols(); ++c) {
-      (*m)(r, c) = 2 * RandDouble() - 1;
-    }
-  }
-}
-
-}  // namespace
-
 TEST(NormalPriorTest, ResidualAtRandomPosition) {
-  srand(5);
-
+  std::mt19937 prng;
+  std::uniform_real_distribution distribution(-1.0, 1.0);
   for (int num_rows = 1; num_rows < 5; ++num_rows) {
     for (int num_cols = 1; num_cols < 5; ++num_cols) {
       Vector b(num_cols);
-      RandomVector(&b);
-
+      b.setRandom();
       Matrix A(num_rows, num_cols);
-      RandomMatrix(&A);
+      A.setRandom();
 
       auto* x = new double[num_cols];
-      for (int i = 0; i < num_cols; ++i) x[i] = 2 * RandDouble() - 1;
+      for (int i = 0; i < num_cols; ++i) x[i] = distribution(prng);
 
       auto* jacobian = new double[num_rows * num_cols];
       Vector residuals(num_rows);
@@ -92,18 +75,17 @@
 }
 
 TEST(NormalPriorTest, ResidualAtRandomPositionNullJacobians) {
-  srand(5);
-
+  std::mt19937 prng;
+  std::uniform_real_distribution distribution(-1.0, 1.0);
   for (int num_rows = 1; num_rows < 5; ++num_rows) {
     for (int num_cols = 1; num_cols < 5; ++num_cols) {
       Vector b(num_cols);
-      RandomVector(&b);
-
+      b.setRandom();
       Matrix A(num_rows, num_cols);
-      RandomMatrix(&A);
+      A.setRandom();
 
       auto* x = new double[num_cols];
-      for (int i = 0; i < num_cols; ++i) x[i] = 2 * RandDouble() - 1;
+      for (int i = 0; i < num_cols; ++i) x[i] = distribution(prng);
 
       double* jacobians[1];
       jacobians[0] = nullptr;
diff --git a/internal/ceres/partitioned_matrix_view_test.cc b/internal/ceres/partitioned_matrix_view_test.cc
index d01dd3b..6e9dd92 100644
--- a/internal/ceres/partitioned_matrix_view_test.cc
+++ b/internal/ceres/partitioned_matrix_view_test.cc
@@ -31,13 +31,13 @@
 #include "ceres/partitioned_matrix_view.h"
 
 #include <memory>
+#include <random>
 #include <vector>
 
 #include "ceres/block_structure.h"
 #include "ceres/casts.h"
 #include "ceres/internal/eigen.h"
 #include "ceres/linear_least_squares_problems.h"
-#include "ceres/random.h"
 #include "ceres/sparse_matrix.h"
 #include "glog/logging.h"
 #include "gtest/gtest.h"
@@ -50,7 +50,6 @@
 class PartitionedMatrixViewTest : public ::testing::Test {
  protected:
   void SetUp() final {
-    srand(5);
     std::unique_ptr<LinearLeastSquaresProblem> problem =
         CreateLinearLeastSquaresProblemFromId(2);
     CHECK(problem != nullptr);
@@ -65,11 +64,16 @@
         options, *down_cast<BlockSparseMatrix*>(A_.get()));
   }
 
+  double RandDouble() { return distribution_(prng_); }
+
   int num_rows_;
   int num_cols_;
   int num_eliminate_blocks_;
   std::unique_ptr<SparseMatrix> A_;
   std::unique_ptr<PartitionedMatrixViewBase> pmv_;
+  std::mt19937 prng_;
+  std::uniform_real_distribution<double> distribution_ =
+      std::uniform_real_distribution(0.0, 1.0);
 };
 
 TEST_F(PartitionedMatrixViewTest, DimensionsTest) {
diff --git a/internal/ceres/random.h b/internal/ceres/random.h
deleted file mode 100644
index 0495d67..0000000
--- a/internal/ceres/random.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
-// http://ceres-solver.org/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// * Redistributions of source code must retain the above copyright notice,
-//   this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright notice,
-//   this list of conditions and the following disclaimer in the documentation
-//   and/or other materials provided with the distribution.
-// * Neither the name of Google Inc. nor the names of its contributors may be
-//   used to endorse or promote products derived from this software without
-//   specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: keir@google.com (Keir Mierle)
-//         sameeragarwal@google.com (Sameer Agarwal)
-
-#ifndef CERES_INTERNAL_RANDOM_H_
-#define CERES_INTERNAL_RANDOM_H_
-
-#include <cmath>
-#include <cstdlib>
-
-#include "ceres/internal/export.h"
-
-namespace ceres {
-
-inline void SetRandomState(int state) { srand(state); }
-
-inline int Uniform(int n) {
-  if (n) {
-    return rand() % n;
-  } else {
-    return 0;
-  }
-}
-
-inline double RandDouble() {
-  auto r = static_cast<double>(rand());
-  return r / RAND_MAX;
-}
-
-// Box-Muller algorithm for normal random number generation.
-// http://en.wikipedia.org/wiki/Box-Muller_transform
-inline double RandNormal() {
-  double x1, x2, w;
-  do {
-    x1 = 2.0 * RandDouble() - 1.0;
-    x2 = 2.0 * RandDouble() - 1.0;
-    w = x1 * x1 + x2 * x2;
-  } while (w >= 1.0 || w == 0.0);
-
-  w = sqrt((-2.0 * log(w)) / w);
-  return x1 * w;
-}
-
-}  // namespace ceres
-
-#endif  // CERES_INTERNAL_RANDOM_H_
diff --git a/internal/ceres/schur_eliminator_benchmark.cc b/internal/ceres/schur_eliminator_benchmark.cc
index 2bb18ca..0973e73 100644
--- a/internal/ceres/schur_eliminator_benchmark.cc
+++ b/internal/ceres/schur_eliminator_benchmark.cc
@@ -29,13 +29,13 @@
 // Authors: sameeragarwal@google.com (Sameer Agarwal)
 
 #include <memory>
+#include <random>
 
 #include "Eigen/Dense"
 #include "benchmark/benchmark.h"
 #include "ceres/block_random_access_dense_matrix.h"
 #include "ceres/block_sparse_matrix.h"
 #include "ceres/block_structure.h"
-#include "ceres/random.h"
 #include "ceres/schur_eliminator.h"
 
 namespace ceres::internal {
@@ -91,8 +91,10 @@
 
     matrix_ = std::make_unique<BlockSparseMatrix>(bs);
     double* values = matrix_->mutable_values();
+    std::mt19937 prng;
+    std::normal_distribution<double> standard_normal;
     for (int i = 0; i < matrix_->num_nonzeros(); ++i) {
-      values[i] = RandNormal();
+      values[i] = standard_normal(prng);
     }
 
     b_.resize(matrix_->num_rows());
diff --git a/internal/ceres/schur_eliminator_test.cc b/internal/ceres/schur_eliminator_test.cc
index a8c5f71..da27019 100644
--- a/internal/ceres/schur_eliminator_test.cc
+++ b/internal/ceres/schur_eliminator_test.cc
@@ -31,6 +31,7 @@
 #include "ceres/schur_eliminator.h"
 
 #include <memory>
+#include <random>
 
 #include "Eigen/Dense"
 #include "ceres/block_random_access_dense_matrix.h"
@@ -41,7 +42,6 @@
 #include "ceres/detect_structure.h"
 #include "ceres/internal/eigen.h"
 #include "ceres/linear_least_squares_problems.h"
-#include "ceres/random.h"
 #include "ceres/test_util.h"
 #include "ceres/triplet_sparse_matrix.h"
 #include "ceres/types.h"
@@ -283,8 +283,10 @@
 
   BlockSparseMatrix matrix(bs);
   double* values = matrix.mutable_values();
+  std::mt19937 prng;
+  std::normal_distribution<double> standard_normal;
   for (int i = 0; i < matrix.num_nonzeros(); ++i) {
-    values[i] = RandNormal();
+    values[i] = standard_normal(prng);
   }
 
   Vector b(matrix.num_rows());
diff --git a/internal/ceres/sparse_cholesky_test.cc b/internal/ceres/sparse_cholesky_test.cc
index 0522232..e99d59b 100644
--- a/internal/ceres/sparse_cholesky_test.cc
+++ b/internal/ceres/sparse_cholesky_test.cc
@@ -32,6 +32,7 @@
 
 #include <memory>
 #include <numeric>
+#include <random>
 #include <vector>
 
 #include "Eigen/Dense"
@@ -42,7 +43,6 @@
 #include "ceres/internal/config.h"
 #include "ceres/internal/eigen.h"
 #include "ceres/iterative_refiner.h"
-#include "ceres/random.h"
 #include "glog/logging.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -165,17 +165,18 @@
 class SparseCholeskyTest : public ::testing::TestWithParam<Param> {};
 
 TEST_P(SparseCholeskyTest, FactorAndSolve) {
-  SetRandomState(2982);
   const int kMinNumBlocks = 1;
   const int kMaxNumBlocks = 10;
   const int kNumTrials = 10;
   const int kMinBlockSize = 1;
   const int kMaxBlockSize = 5;
+  std::mt19937 prng;
+  std::uniform_real_distribution distribution(0.1, 1.0);
 
   for (int num_blocks = kMinNumBlocks; num_blocks < kMaxNumBlocks;
        ++num_blocks) {
     for (int trial = 0; trial < kNumTrials; ++trial) {
-      const double block_density = std::max(0.1, RandDouble());
+      const double block_density = distribution(prng);
       Param param = GetParam();
       SparseCholeskySolverUnitTest(::testing::get<0>(param),
                                    ::testing::get<1>(param),
diff --git a/internal/ceres/triplet_sparse_matrix.cc b/internal/ceres/triplet_sparse_matrix.cc
index 86a444e..c5ff2e5 100644
--- a/internal/ceres/triplet_sparse_matrix.cc
+++ b/internal/ceres/triplet_sparse_matrix.cc
@@ -32,12 +32,12 @@
 
 #include <algorithm>
 #include <memory>
+#include <random>
 
 #include "ceres/compressed_row_sparse_matrix.h"
 #include "ceres/crs_matrix.h"
 #include "ceres/internal/eigen.h"
 #include "ceres/internal/export.h"
-#include "ceres/random.h"
 #include "ceres/types.h"
 #include "glog/logging.h"
 
@@ -282,8 +282,8 @@
   }
 }
 
-std::unique_ptr<TripletSparseMatrix>
-TripletSparseMatrix::CreateFromTextFile(FILE* file) {
+std::unique_ptr<TripletSparseMatrix> TripletSparseMatrix::CreateFromTextFile(
+    FILE* file) {
   CHECK(file != nullptr);
   int num_rows = 0;
   int num_cols = 0;
@@ -317,16 +317,19 @@
   std::vector<int> rows;
   std::vector<int> cols;
   std::vector<double> values;
+  std::mt19937 prng;
+  std::uniform_real_distribution uniform01(0.0, 1.0);
+  std::uniform_real_distribution standard_normal;
   while (rows.empty()) {
     rows.clear();
     cols.clear();
     values.clear();
     for (int r = 0; r < options.num_rows; ++r) {
       for (int c = 0; c < options.num_cols; ++c) {
-        if (RandDouble() <= options.density) {
+        if (uniform01(prng) <= options.density) {
           rows.push_back(r);
           cols.push_back(c);
-          values.push_back(RandNormal());
+          values.push_back(standard_normal(prng));
         }
       }
     }