Improvements in Accounting, refactoring in C++ & Go

Accounting:
- Support noise_multiplier=0 and sampling_probability=0 in (Poisson
  subsampled) Gaussian and Laplace DpEvent
- Fix behavior of RdpAccountant when sampling_probability=0 and
  noise_multiplier=0 in Poisson subsampled Gaussian DpEvent
- Add [0, 1] as default lower-endpoint-and-guess bracket interval.
  Remove redundant check of bracket interval, since brentq also raises
  when interval is bad

C++:
- Refactoring of partition selection

Go:
- Add go.sum

Change-Id: I1f6219a51f43218693aa9b32c51b585469a1eb28
GitOrigin-RevId: 6bf7243904c5d31ad6670832912da2c9c19ebc38
diff --git a/cc/algorithms/BUILD b/cc/algorithms/BUILD
index 79b7d8c..b34f208 100644
--- a/cc/algorithms/BUILD
+++ b/cc/algorithms/BUILD
@@ -600,6 +600,7 @@
         ":numerical-mechanisms",
         ":rand",
         ":util",
+        "@com_google_absl//absl/status",
         "@com_google_absl//absl/status:statusor",
         "@com_google_cc_differential_privacy//base:status",
     ],
@@ -610,8 +611,11 @@
     srcs = ["partition-selection_test.cc"],
     shard_count = 2,
     deps = [
+        ":numerical-mechanisms",
         ":numerical-mechanisms-testing",
         ":partition-selection",
+        "//base/testing:status_matchers",
+        "@com_google_absl//absl/memory",
         "@com_google_absl//absl/status:statusor",
         "@com_google_googletest//:gtest_main",
     ],
diff --git a/cc/algorithms/partition-selection.h b/cc/algorithms/partition-selection.h
index 469dddd..e98287f 100644
--- a/cc/algorithms/partition-selection.h
+++ b/cc/algorithms/partition-selection.h
@@ -24,6 +24,7 @@
 #include <string>
 #include <utility>
 
+#include "absl/status/status.h"
 #include "absl/status/statusor.h"
 #include "algorithms/distributions.h"
 #include "algorithms/numerical-mechanisms.h"
@@ -33,65 +34,13 @@
 
 namespace differential_privacy {
 
-// Provides a common abstraction for PartitionSelectionStrategy. Each partition
-// selection strategy class has a builder with which it can be instantiated, and
-// calling ShouldKeep will return true if a partition with the given number of
-// users should be kept based on the values the partition selection strategy
-// was instantiated with (while ShouldKeep will return false if the partition
-// should have been dropped).
+// Provides a common abstraction for PartitionSelectionStrategy. Calling
+// ShouldKeep will return true if a partition with the given number of users
+// should be kept based on the values the partition selection strategy was
+// instantiated with (while ShouldKeep will return false if the partition should
+// have been dropped).
 class PartitionSelectionStrategy {
  public:
-  // Builder base class
-  class Builder {
-   public:
-    virtual ~Builder() = default;
-
-    Builder& SetEpsilon(double epsilon) {
-      epsilon_ = epsilon;
-      return *this;
-    }
-
-    Builder& SetDelta(double delta) {
-      delta_ = delta;
-      return *this;
-    }
-
-    Builder& SetMaxPartitionsContributed(int64_t max_partitions_contributed) {
-      max_partitions_contributed_ = max_partitions_contributed;
-      return *this;
-    }
-
-    virtual absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>>
-    Build() = 0;
-
-   protected:
-    // Convenience methods to check if the Builder variables are set & valid
-    absl::Status EpsilonIsSetAndValid() {
-      return PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon_);
-    }
-    absl::Status DeltaIsSetAndValid() {
-      return PartitionSelectionStrategy::DeltaIsSetAndValid(delta_);
-    }
-    absl::Status MaxPartitionsContributedIsSetAndValid() {
-      return PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-          max_partitions_contributed_);
-    }
-
-    absl::optional<double> GetEpsilon() { return epsilon_; }
-
-    absl::optional<double> GetDelta() { return delta_; }
-
-    absl::optional<int64_t> GetMaxPartitionsContributed() {
-      return max_partitions_contributed_;
-    }
-
-    absl::optional<double> delta_;
-
-   private:
-    absl::optional<double> epsilon_;
-    absl::optional<int64_t> max_partitions_contributed_;
-  };
-
   virtual ~PartitionSelectionStrategy() = default;
 
   double GetEpsilon() const { return epsilon_; }
@@ -102,6 +51,9 @@
     return max_partitions_contributed_;
   }
 
+  // This is set with the results from `CalculateAdjustedDelta`.
+  double GetAdjustedDelta() const { return adjusted_delta_; }
+
   // ShouldKeep returns true when a partition with a given number of users
   // should be kept and false otherwise.
   virtual bool ShouldKeep(double num_users) = 0;
@@ -117,29 +69,6 @@
         max_partitions_contributed_(max_partitions_contributed),
         adjusted_delta_(adjusted_delta) {}
 
-  // Checks if epsilon is set and valid.
-  static absl::Status EpsilonIsSetAndValid(absl::optional<double> epsilon) {
-    RETURN_IF_ERROR(ValidateIsFiniteAndPositive(epsilon, "Epsilon"));
-    return absl::OkStatus();
-  }
-
-  // Checks if delta is set and valid.
-  static absl::Status DeltaIsSetAndValid(absl::optional<double> delta) {
-    RETURN_IF_ERROR(ValidateIsInInclusiveInterval(delta, 0, 1, "Delta"));
-    return absl::OkStatus();
-  }
-
-  // Checks if the max number of partitions contributed to is set and valid.
-  static absl::Status MaxPartitionsContributedIsSetAndValid(
-      absl::optional<int64_t> max_partitions_contributed) {
-    RETURN_IF_ERROR(ValidateIsPositive(
-        max_partitions_contributed,
-        "Max number of partitions a user can contribute to"));
-    return absl::OkStatus();
-  }
-
-  double GetAdjustedDelta() const { return adjusted_delta_; }
-
   // We must derive an adjusted delta, to be used as the probability of keeping
   // a single partition with one user, from delta, the probability we keep any
   // of the partitions contributed to by a single user.  Since the probability
@@ -148,10 +77,9 @@
   // contribute to will get us delta, we can solve to get the following formula.
   static absl::StatusOr<double> CalculateAdjustedDelta(
       double delta, int64_t max_partitions_contributed) {
-    RETURN_IF_ERROR(PartitionSelectionStrategy::DeltaIsSetAndValid(delta));
+    RETURN_IF_ERROR(ValidateDelta(delta));
     RETURN_IF_ERROR(
-        PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-            max_partitions_contributed));
+        ValidateMaxPartitionsContributed(max_partitions_contributed));
 
     // Numerically stable equivalent of
     // 1- pow(1 - delta, 1 / max_partitions_contributed).
@@ -164,11 +92,9 @@
   // Inverse of CalculateAdjustedDelta()
   static absl::StatusOr<double> CalculateUnadjustedDelta(
       double adjusted_delta, int64_t max_partitions_contributed) {
+    RETURN_IF_ERROR(ValidateDelta(adjusted_delta));
     RETURN_IF_ERROR(
-        PartitionSelectionStrategy::DeltaIsSetAndValid(adjusted_delta));
-    RETURN_IF_ERROR(
-        PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-            max_partitions_contributed));
+        ValidateMaxPartitionsContributed(max_partitions_contributed));
 
     // Numerically stable equivalent of
     // 1 - pow(1 - adjusted_delta, max_partitions_contributed).
@@ -185,6 +111,46 @@
   double adjusted_delta_;
 };
 
+// Provides a common abstraction for PartitionSelectionStrategy builders. Each
+// partition selection strategy builder inherits from this builder.
+class PartitionSelectionStrategyBuilder {
+ public:
+  virtual ~PartitionSelectionStrategyBuilder() = default;
+
+  PartitionSelectionStrategyBuilder& SetEpsilon(double epsilon) {
+    epsilon_ = epsilon;
+    return *this;
+  }
+
+  PartitionSelectionStrategyBuilder& SetDelta(double delta) {
+    delta_ = delta;
+    return *this;
+  }
+
+  PartitionSelectionStrategyBuilder& SetMaxPartitionsContributed(
+      int64_t max_partitions_contributed) {
+    max_partitions_contributed_ = max_partitions_contributed;
+    return *this;
+  }
+
+  virtual absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>>
+  Build() = 0;
+
+ protected:
+  std::optional<double> GetEpsilon() { return epsilon_; }
+
+  std::optional<double> GetDelta() { return delta_; }
+
+  std::optional<int64_t> GetMaxPartitionsContributed() {
+    return max_partitions_contributed_;
+  }
+
+ private:
+  std::optional<double> epsilon_;
+  std::optional<double> delta_;
+  std::optional<int64_t> max_partitions_contributed_;
+};
+
 // NearTruncatedGeometricPartitionSelection implements magic partition selection
 // - instead of calculating a specific threshold to determine whether or not a
 // partition should be kept, magic partition selection uses a formula derived
@@ -198,13 +164,14 @@
     : public PartitionSelectionStrategy {
  public:
   // Builder for NearTruncatedGeometricPartitionSelection
-  class Builder : public PartitionSelectionStrategy::Builder {
+  class Builder : public PartitionSelectionStrategyBuilder {
    public:
     absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>> Build()
         override {
-      RETURN_IF_ERROR(EpsilonIsSetAndValid());
-      RETURN_IF_ERROR(DeltaIsSetAndValid());
-      RETURN_IF_ERROR(MaxPartitionsContributedIsSetAndValid());
+      RETURN_IF_ERROR(ValidateEpsilon(GetEpsilon()));
+      RETURN_IF_ERROR(ValidateDelta(GetDelta()));
+      RETURN_IF_ERROR(
+          ValidateMaxPartitionsContributed(GetMaxPartitionsContributed()));
 
       ASSIGN_OR_RETURN(
           double adjusted_delta,
@@ -278,7 +245,6 @@
   double adjusted_epsilon_;
   double crossover_1_;
   double crossover_2_;
-
 };
 
 // PreaggPartitionSelection is the deprecated name for
@@ -292,7 +258,7 @@
 class LaplacePartitionSelection : public PartitionSelectionStrategy {
  public:
   // Builder for LaplacePartitionSelection
-  class Builder : public PartitionSelectionStrategy::Builder {
+  class Builder : public PartitionSelectionStrategyBuilder {
    public:
     Builder& SetLaplaceMechanism(
         std::unique_ptr<LaplaceMechanism::Builder> laplace_builder) {
@@ -302,9 +268,11 @@
 
     absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>> Build()
         override {
-      RETURN_IF_ERROR(EpsilonIsSetAndValid());
-      RETURN_IF_ERROR(DeltaIsSetAndValid());
-      RETURN_IF_ERROR(MaxPartitionsContributedIsSetAndValid());
+      RETURN_IF_ERROR(ValidateEpsilon(GetEpsilon()));
+      RETURN_IF_ERROR(ValidateDelta(GetDelta()));
+      RETURN_IF_ERROR(
+          ValidateMaxPartitionsContributed(GetMaxPartitionsContributed()));
+
       if (laplace_builder_ == nullptr) {
         laplace_builder_ = absl::make_unique<LaplaceMechanism::Builder>();
       }
@@ -357,10 +325,9 @@
 
   static absl::StatusOr<double> CalculateDelta(
       double epsilon, double threshold, int64_t max_partitions_contributed) {
-    RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
+    RETURN_IF_ERROR(ValidateEpsilon(epsilon));
     RETURN_IF_ERROR(
-        PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-            max_partitions_contributed));
+        ValidateMaxPartitionsContributed(max_partitions_contributed));
 
     if (threshold < 1) {
       return CalculateUnadjustedDelta(
@@ -380,11 +347,10 @@
 
   static absl::StatusOr<double> CalculateThreshold(
       double epsilon, double delta, int64_t max_partitions_contributed) {
-    RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
-    RETURN_IF_ERROR(PartitionSelectionStrategy::DeltaIsSetAndValid(delta));
+    RETURN_IF_ERROR(ValidateEpsilon(epsilon));
+    RETURN_IF_ERROR(ValidateDelta(delta));
     RETURN_IF_ERROR(
-        PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-            max_partitions_contributed));
+        ValidateMaxPartitionsContributed(max_partitions_contributed));
 
     ASSIGN_OR_RETURN(double adjusted_delta,
                      CalculateAdjustedDelta(delta, max_partitions_contributed));
@@ -435,7 +401,7 @@
 class GaussianPartitionSelection : public PartitionSelectionStrategy {
  public:
   // Builder for GaussianPartitionSelection
-  class Builder : public PartitionSelectionStrategy::Builder {
+  class Builder : public PartitionSelectionStrategyBuilder {
    public:
     Builder& SetGaussianMechanism(
         std::unique_ptr<GaussianMechanism::Builder> gaussian_builder) {
@@ -445,9 +411,10 @@
 
     absl::StatusOr<std::unique_ptr<PartitionSelectionStrategy>> Build()
         override {
-      RETURN_IF_ERROR(EpsilonIsSetAndValid());
-      RETURN_IF_ERROR(DeltaIsSetAndValid());
-      RETURN_IF_ERROR(MaxPartitionsContributedIsSetAndValid());
+      RETURN_IF_ERROR(ValidateEpsilon(GetEpsilon()));
+      RETURN_IF_ERROR(ValidateDelta(GetDelta()));
+      RETURN_IF_ERROR(
+          ValidateMaxPartitionsContributed(GetMaxPartitionsContributed()));
       if (gaussian_builder_ == nullptr) {
         gaussian_builder_ = absl::make_unique<GaussianMechanism::Builder>();
       }
@@ -512,11 +479,10 @@
   static absl::StatusOr<double> CalculateThresholdDelta(
       double epsilon, double noise_delta, double threshold,
       int64_t max_partitions_contributed) {
-    RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
-    RETURN_IF_ERROR(DeltaIsSetAndValid(noise_delta));
+    RETURN_IF_ERROR(ValidateEpsilon(epsilon));
+    RETURN_IF_ERROR(ValidateDelta(noise_delta));
     RETURN_IF_ERROR(
-        PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-            max_partitions_contributed));
+        ValidateMaxPartitionsContributed(max_partitions_contributed));
 
     double sigma = GaussianMechanism::CalculateStddev(
         epsilon, noise_delta, max_partitions_contributed);
@@ -539,12 +505,11 @@
   static absl::StatusOr<double> CalculateThreshold(
       double epsilon, double noise_delta, double threshold_delta,
       int64_t max_partitions_contributed) {
-    RETURN_IF_ERROR(PartitionSelectionStrategy::EpsilonIsSetAndValid(epsilon));
-    RETURN_IF_ERROR(DeltaIsSetAndValid(noise_delta));
-    RETURN_IF_ERROR(DeltaIsSetAndValid(threshold_delta));
+    RETURN_IF_ERROR(ValidateEpsilon(epsilon));
+    RETURN_IF_ERROR(ValidateDelta(noise_delta));
+    RETURN_IF_ERROR(ValidateDelta(threshold_delta));
     RETURN_IF_ERROR(
-        PartitionSelectionStrategy::MaxPartitionsContributedIsSetAndValid(
-            max_partitions_contributed));
+        ValidateMaxPartitionsContributed(max_partitions_contributed));
 
     double sigma = GaussianMechanism::CalculateStddev(
         epsilon, noise_delta, max_partitions_contributed);
@@ -580,7 +545,6 @@
   double threshold_;
   std::unique_ptr<NumericalMechanism> mechanism_;
 };
-
 }  // namespace differential_privacy
 
 #endif  // DIFFERENTIAL_PRIVACY_CPP_ALGORITHMS_PARTITION_SELECTION_H_
diff --git a/cc/algorithms/partition-selection_test.cc b/cc/algorithms/partition-selection_test.cc
index f4c7a10..000e485 100644
--- a/cc/algorithms/partition-selection_test.cc
+++ b/cc/algorithms/partition-selection_test.cc
@@ -19,10 +19,12 @@
 #include <cmath>
 #include <cstdlib>
 
+#include "base/testing/status_matchers.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/memory/memory.h"
 #include "absl/status/statusor.h"
-#include "algorithms/numerical-mechanisms-testing.h"
+#include "algorithms/numerical-mechanisms.h"
 
 namespace differential_privacy {
 namespace {
@@ -30,7 +32,8 @@
 using ::testing::DoubleEq;
 using ::testing::DoubleNear;
 using ::testing::Eq;
-using ::testing::MatchesRegex;
+using ::testing::HasSubstr;
+using ::differential_privacy::base::testing::StatusIs;
 
 constexpr int kNumSamples = 10000000;
 constexpr int kSmallNumSamples = 1000000;
@@ -52,143 +55,122 @@
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionUnsetEpsilon) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build =
-      test_builder.SetDelta(0.1).SetMaxPartitionsContributed(2).Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Epsilon must be set.*"));
+  EXPECT_THAT(test_builder.SetDelta(0.1).SetMaxPartitionsContributed(2).Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Epsilon must be set")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionNanEpsilon) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(NAN)
-                          .SetDelta(0.3)
-                          .SetMaxPartitionsContributed(4)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Epsilon must be a valid numeric value.*"));
+  EXPECT_THAT(test_builder.SetEpsilon(NAN)
+                  .SetDelta(0.3)
+                  .SetMaxPartitionsContributed(4)
+                  .Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Epsilon must be a valid numeric value")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionNotFiniteEpsilon) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build =
-      test_builder.SetEpsilon(std::numeric_limits<double>::infinity())
-          .SetDelta(0.3)
-          .SetMaxPartitionsContributed(4)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Epsilon must be finite.*"));
+  EXPECT_THAT(test_builder.SetEpsilon(std::numeric_limits<double>::infinity())
+                  .SetDelta(0.3)
+                  .SetMaxPartitionsContributed(4)
+                  .Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Epsilon must be finite")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionNegativeEpsilon) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(-5.0)
-                          .SetDelta(0.6)
-                          .SetMaxPartitionsContributed(7)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Epsilon must be finite and positive.*"));
+  EXPECT_THAT(test_builder.SetEpsilon(-5.0)
+                  .SetDelta(0.6)
+                  .SetMaxPartitionsContributed(7)
+                  .Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Epsilon must be finite and positive")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionUnsetDelta) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build =
-      test_builder.SetEpsilon(8.0).SetMaxPartitionsContributed(9).Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Delta must be set.*"));
+  EXPECT_THAT(
+      test_builder.SetEpsilon(8.0).SetMaxPartitionsContributed(9).Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be set.")));
 }
 
 TEST(PartitionSelectionTest, NearTruncatedGeometricPartitionSelectionNanDelta) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(1.2)
-                          .SetDelta(NAN)
-                          .SetMaxPartitionsContributed(3)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Delta must be a valid numeric value.*"));
+  EXPECT_THAT(test_builder.SetEpsilon(1.2)
+                  .SetDelta(NAN)
+                  .SetMaxPartitionsContributed(3)
+                  .Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Delta must be a valid numeric value")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionNotFiniteDelta) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(1.2)
-                          .SetDelta(std::numeric_limits<double>::infinity())
-                          .SetMaxPartitionsContributed(3)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+  EXPECT_THAT(test_builder.SetEpsilon(1.2)
+                  .SetDelta(std::numeric_limits<double>::infinity())
+                  .SetMaxPartitionsContributed(3)
+                  .Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionInvalidDelta) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(4.5)
-                          .SetDelta(6.0)
-                          .SetMaxPartitionsContributed(7)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+  EXPECT_THAT(test_builder.SetEpsilon(4.5)
+                  .SetDelta(6.0)
+                  .SetMaxPartitionsContributed(7)
+                  .Build(),
+              StatusIs(absl::StatusCode::kInvalidArgument,
+                       HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionUnsetMaxPartitionsContributed) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(0.8).SetDelta(0.9).Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be set.*"));
+  EXPECT_THAT(
+      test_builder.SetEpsilon(0.8).SetDelta(0.9).Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be set")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionNegativeMaxPartitionsContributed) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(0.1)
-                          .SetDelta(0.2)
-                          .SetMaxPartitionsContributed(-3)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be positive.*"));
+  EXPECT_THAT(
+      test_builder.SetEpsilon(0.1)
+          .SetDelta(0.2)
+          .SetMaxPartitionsContributed(-3)
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be positive, but is -3.")));
 }
 
 TEST(PartitionSelectionTest,
      NearTruncatedGeometricPartitionSelectionZeroMaxPartitionsContributed) {
   NearTruncatedGeometricPartitionSelection::Builder test_builder;
-  auto failed_build = test_builder.SetEpsilon(0.1)
-                          .SetDelta(0.2)
-                          .SetMaxPartitionsContributed(0)
-                          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be positive.*"));
+  EXPECT_THAT(
+      test_builder.SetEpsilon(0.1)
+          .SetDelta(0.2)
+          .SetMaxPartitionsContributed(0)
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be positive, but is 0.")));
 }
 
 // We expect the probability of keeping a partition with one user
@@ -365,142 +347,124 @@
 TEST(PartitionSelectionTest,
      LaplacePartitionSelectionUnsetMaxPartitionsContributed) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetDelta(0.1)
           .SetEpsilon(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be set.*"));
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be set")));
 }
 
 TEST(PartitionSelectionTest,
      LaplacePartitionSelectionNegativeMaxPartitionsContributed) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetDelta(0.1)
           .SetEpsilon(2)
           .SetMaxPartitionsContributed(-3)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be positive.*"));
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be positive, but is -3.")));
 }
 
 TEST(PartitionSelectionTest,
      LaplacePartitionSelectionZeroMaxPartitionsContributed) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetDelta(0.1)
           .SetEpsilon(2)
           .SetMaxPartitionsContributed(0)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be positive.*"));
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be positive, but is 0.")));
 }
 
 TEST(PartitionSelectionTest, LaplacePartitionSelectionUnsetEpsilon) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetDelta(0.1)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Epsilon must be set.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Epsilon must be set.")));
 }
 
 TEST(PartitionSelectionTest, LaplacePartitionSelectionUnsetDelta) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Delta must be set.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be set")));
 }
 
 TEST(PartitionSelectionTest, LaplacePartitionSelectionNanDelta) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(NAN)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Delta must be a valid numeric value.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be a valid numeric value")));
 }
 
 TEST(PartitionSelectionTest, LaplacePartitionSelectionNotFiniteDelta) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(std::numeric_limits<double>::infinity())
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest, LaplacePartitionSelectionInvalidPositiveDelta) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(5.2)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest, LaplacePartitionSelectionInvalidNegativeDelta) {
   LaplacePartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetLaplaceMechanism(absl::make_unique<LaplaceMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(-0.1)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be in the inclusive interval")));
 }
 
 // We expect the probability of keeping a partition with one user
@@ -1124,142 +1088,124 @@
 TEST(PartitionSelectionTest,
      GaussianPartitionSelectionUnsetMaxPartitionsContributed) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetDelta(0.1)
           .SetEpsilon(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be set.*"));
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be set")));
 }
 
 TEST(PartitionSelectionTest,
      GaussianPartitionSelectionNegativeMaxPartitionsContributed) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetDelta(0.1)
           .SetEpsilon(2)
           .SetMaxPartitionsContributed(-3)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be positive.*"));
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be positive")));
 }
 
 TEST(PartitionSelectionTest,
      GaussianPartitionSelectionZeroMaxPartitionsContributed) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetDelta(0.1)
           .SetEpsilon(2)
           .SetMaxPartitionsContributed(0)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Max number of partitions a user can"
-                                    " contribute to must be positive.*"));
+          .Build(),
+      StatusIs(
+          absl::StatusCode::kInvalidArgument,
+          HasSubstr("Maximum number of partitions that can be contributed to "
+                    "(i.e., L0 sensitivity) must be positive, but is 0.")));
 }
 
 TEST(PartitionSelectionTest, GaussianPartitionSelectionUnsetEpsilon) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetDelta(0.1)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Epsilon must be set.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Epsilon must be set.")));
 }
 
 TEST(PartitionSelectionTest, GaussianPartitionSelectionUnsetDelta) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Delta must be set.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be set.")));
 }
 
 TEST(PartitionSelectionTest, GaussianPartitionSelectionNanDelta) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(NAN)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message, MatchesRegex("^Delta must be a valid numeric value.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be a valid numeric value")));
 }
 
 TEST(PartitionSelectionTest, GaussianPartitionSelectionNotFiniteDelta) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(std::numeric_limits<double>::infinity())
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest, GaussianPartitionSelectionInvalidPositiveDelta) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(5.2)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest, GaussianPartitionSelectionInvalidNegativeDelta) {
   GaussianPartitionSelection::Builder test_builder;
-  auto failed_build =
+  EXPECT_THAT(
       test_builder
           .SetGaussianMechanism(absl::make_unique<GaussianMechanism::Builder>())
           .SetEpsilon(0.1)
           .SetDelta(-0.1)
           .SetMaxPartitionsContributed(2)
-          .Build();
-  EXPECT_THAT(failed_build.status().code(),
-              Eq(absl::StatusCode::kInvalidArgument));
-  std::string message(std::string(failed_build.status().message()));
-  EXPECT_THAT(message,
-              MatchesRegex("^Delta must be in the inclusive interval.*"));
+          .Build(),
+      StatusIs(absl::StatusCode::kInvalidArgument,
+               HasSubstr("Delta must be in the inclusive interval")));
 }
 
 TEST(PartitionSelectionTest, CalculateGaussianThresholdTests) {
diff --git a/cc/algorithms/util.cc b/cc/algorithms/util.cc
index fb6dfe9..6d3fae0 100644
--- a/cc/algorithms/util.cc
+++ b/cc/algorithms/util.cc
@@ -18,6 +18,7 @@
 
 #include <cmath>
 #include <cstdlib>
+#include <memory>
 #include <vector>
 
 #include "absl/status/status.h"
@@ -323,5 +324,4 @@
   return ValidateIsGreaterThanOrEqualTo(branching_factor, /*lower_bound=*/2,
                                         "Branching Factor");
 }
-
 }  // namespace differential_privacy
diff --git a/cc/algorithms/util.h b/cc/algorithms/util.h
index 9aeb72c..ceb99fc 100644
--- a/cc/algorithms/util.h
+++ b/cc/algorithms/util.h
@@ -546,7 +546,6 @@
   }
   return absl::OkStatus();
 }
-
 }  // namespace differential_privacy
 
 #endif  // DIFFERENTIAL_PRIVACY_ALGORITHMS_UTIL_H_
diff --git a/go/go.sum b/go/go.sum
new file mode 100644
index 0000000..d88af24
--- /dev/null
+++ b/go/go.sum
@@ -0,0 +1,29 @@
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
+github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/grd/stat v0.0.0-20130623202159-138af3fd5012 h1:TVY1GBBIAAph4RWO9Y3p1wU+7n6khY1jxPKjDphzznA=
+github.com/grd/stat v0.0.0-20130623202159-138af3fd5012/go.mod h1:hHyH5N67TF4tD4PBbqMlyuIu5Lq5QwKSgNyyG31trzY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=
+golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o=
+gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY=
\ No newline at end of file
diff --git a/java/dp_java_deps_preload.bzl b/java/dp_java_deps_preload.bzl
index 1a80d57..20e21bf 100644
--- a/java/dp_java_deps_preload.bzl
+++ b/java/dp_java_deps_preload.bzl
@@ -23,4 +23,3 @@
         sha256 = BAZEL_COMMON_SHA,
         strip_prefix = "bazel-common-%s" % BAZEL_COMMON_TAG,
     )
-
diff --git a/python/dp_accounting/mechanism_calibration.py b/python/dp_accounting/mechanism_calibration.py
index 93dafba..055788c 100644
--- a/python/dp_accounting/mechanism_calibration.py
+++ b/python/dp_accounting/mechanism_calibration.py
@@ -106,10 +106,9 @@
                                  Callable[[int], dp_event.DpEvent]],
     target_epsilon: float,
     target_delta: float,
-    bracket_interval: BracketInterval,
+    bracket_interval: Optional[BracketInterval] = None,
     discrete: bool = False,
-    tol: Optional[float] = None
-) -> Union[float, int]:
+    tol: Optional[float] = None) -> Union[float, int]:
   """Searches for optimal mechanism parameter value within privacy budget.
 
   The procedure searches over the space of parameters by creating, for each
@@ -131,7 +130,8 @@
     target_epsilon: The target epsilon value.
     target_delta: The target delta value.
     bracket_interval: A BracketInterval used to determine the upper and lower
-      endpoints of the interval within which Brent's method will search.
+      endpoints of the interval within which Brent's method will search. If
+      None, searches for a non-negative bracket starting from [0, 1].
     discrete: A bool determining whether the parameter is continuous or discrete
       valued. If True, the parameter is assumed to take only integer values.
       Concretely, `discrete=True` has three effects. 1) ints, not floats are
@@ -173,6 +173,9 @@
     raise ValueError(f'target_delta must be in range [0, 1]. Found '
                      f'{target_delta}.')
 
+  if bracket_interval is None:
+    bracket_interval = LowerEndpointAndGuess(0, 1)
+
   if tol is None:
     tol = 0.5 if discrete else 1e-6
   elif discrete:
@@ -196,19 +199,17 @@
     raise TypeError(f'Unrecognized bracket_interval type: '
                     f'{type(bracket_interval)}')
 
-  value_1 = epsilon_gap(bracket_interval.endpoint_1)
-  value_2 = epsilon_gap(bracket_interval.endpoint_2)
-  if value_1 * value_2 > 0:
+  try:
+    root, result = optimize.brentq(
+        epsilon_gap,
+        bracket_interval.endpoint_1,
+        bracket_interval.endpoint_2,
+        xtol=tol,
+        full_output=True)
+  except ValueError as err:
     raise ValueError(
-        f'Bracket endpoints do not bracket target_epsilon={target_epsilon}: '
-        f'endpoint 1 {bracket_interval.endpoint_1} with epsilon='
-        f'{value_1 + target_epsilon}, and endpoint 2 '
-        f'{bracket_interval.endpoint_2} with epsilon={value_2 + target_epsilon}'
-    )
-
-  root, result = optimize.brentq(epsilon_gap, bracket_interval.endpoint_1,
-                                 bracket_interval.endpoint_2, xtol=tol,
-                                 full_output=True)
+        '`brentq` raised ValueError. This often means the supplied bracket '
+        f'interval {bracket_interval} did not bracket a solution.') from err
 
   if not result.converged:
     raise NoOptimumFoundError(
diff --git a/python/dp_accounting/mechanism_calibration_test.py b/python/dp_accounting/mechanism_calibration_test.py
index 5975d3e..ad4b86d 100644
--- a/python/dp_accounting/mechanism_calibration_test.py
+++ b/python/dp_accounting/mechanism_calibration_test.py
@@ -167,12 +167,12 @@
           mechanism_calibration.ExplicitBracketInterval(0, 5))
 
   def test_bad_bracket_interval(self):
-    with self.assertRaisesRegex(ValueError, 'Bracket endpoints'):
+    with self.assertRaisesRegex(ValueError, 'did not bracket a solution'):
       mechanism_calibration.calibrate_dp_mechanism(
           lambda: MockAccountant(lambda x: x), MockEvent, 1.0, 0.0,
           mechanism_calibration.ExplicitBracketInterval(2, 5))
 
-    with self.assertRaisesRegex(ValueError, 'Bracket endpoints'):
+    with self.assertRaisesRegex(ValueError, 'did not bracket a solution'):
       mechanism_calibration.calibrate_dp_mechanism(
           lambda: MockAccountant(lambda x: x), MockEvent, 1.0, 0.0,
           mechanism_calibration.ExplicitBracketInterval(-2, 0))
diff --git a/python/dp_accounting/pld/pld_privacy_accountant.py b/python/dp_accounting/pld/pld_privacy_accountant.py
index 91d620c..81afbfc 100644
--- a/python/dp_accounting/pld/pld_privacy_accountant.py
+++ b/python/dp_accounting/pld/pld_privacy_accountant.py
@@ -57,18 +57,24 @@
       return None
     elif isinstance(event, dp_event.GaussianDpEvent):
       if do_compose:
-        gaussian_pld = PLD.from_gaussian_mechanism(
-            standard_deviation=event.noise_multiplier / math.sqrt(count),
-            value_discretization_interval=self._value_discretization_interval)
-        self._pld = self._pld.compose(gaussian_pld)
+        if event.noise_multiplier == 0:
+          self._contains_non_dp_event = True
+        else:
+          gaussian_pld = PLD.from_gaussian_mechanism(
+              standard_deviation=event.noise_multiplier / math.sqrt(count),
+              value_discretization_interval=self._value_discretization_interval)
+          self._pld = self._pld.compose(gaussian_pld)
       return None
     elif isinstance(event, dp_event.LaplaceDpEvent):
       if do_compose:
-        laplace_pld = PLD.from_laplace_mechanism(
-            parameter=event.noise_multiplier,
-            value_discretization_interval=self._value_discretization_interval
-        ).self_compose(count)
-        self._pld = self._pld.compose(laplace_pld)
+        if event.noise_multiplier == 0:
+          self._contains_non_dp_event = True
+        else:
+          laplace_pld = PLD.from_laplace_mechanism(
+              parameter=event.noise_multiplier,
+              value_discretization_interval=self._value_discretization_interval
+          ).self_compose(count)
+          self._pld = self._pld.compose(laplace_pld)
       return None
     elif isinstance(event, dp_event.PoissonSampledDpEvent):
       if self.neighboring_relation != NeighborRel.ADD_OR_REMOVE_ONE:
@@ -79,19 +85,31 @@
             invalid_event=event, error_message=error_msg)
       if isinstance(event.event, dp_event.GaussianDpEvent):
         if do_compose:
-          subsampled_gaussian_pld = PLD.from_gaussian_mechanism(
-              standard_deviation=event.event.noise_multiplier,
-              value_discretization_interval=self._value_discretization_interval,
-              sampling_prob=event.sampling_probability).self_compose(count)
-          self._pld = self._pld.compose(subsampled_gaussian_pld)
+          if event.sampling_probability == 0:
+            pass
+          elif event.event.noise_multiplier == 0:
+            self._contains_non_dp_event = True
+          else:
+            subsampled_gaussian_pld = PLD.from_gaussian_mechanism(
+                standard_deviation=event.event.noise_multiplier,
+                value_discretization_interval=self
+                ._value_discretization_interval,
+                sampling_prob=event.sampling_probability).self_compose(count)
+            self._pld = self._pld.compose(subsampled_gaussian_pld)
         return None
       elif isinstance(event.event, dp_event.LaplaceDpEvent):
         if do_compose:
-          subsampled_laplace_pld = PLD.from_laplace_mechanism(
-              parameter=event.event.noise_multiplier,
-              value_discretization_interval=self._value_discretization_interval,
-              sampling_prob=event.sampling_probability).self_compose(count)
-          self._pld = self._pld.compose(subsampled_laplace_pld)
+          if event.sampling_probability == 0:
+            pass
+          elif event.event.noise_multiplier == 0:
+            self._contains_non_dp_event = True
+          else:
+            subsampled_laplace_pld = PLD.from_laplace_mechanism(
+                parameter=event.event.noise_multiplier,
+                value_discretization_interval=self
+                ._value_discretization_interval,
+                sampling_prob=event.sampling_probability).self_compose(count)
+            self._pld = self._pld.compose(subsampled_laplace_pld)
         return None
       else:
         return CompositionErrorDetails(
diff --git a/python/dp_accounting/pld/pld_privacy_accountant_test.py b/python/dp_accounting/pld/pld_privacy_accountant_test.py
index 784f02b..9cee09a 100644
--- a/python/dp_accounting/pld/pld_privacy_accountant_test.py
+++ b/python/dp_accounting/pld/pld_privacy_accountant_test.py
@@ -51,6 +51,28 @@
     with self.assertRaises(ValueError):
       accountant.compose(event, count)
 
+  @parameterized.parameters(
+      dp_event.GaussianDpEvent(0),
+      dp_event.LaplaceDpEvent(0),
+      dp_event.PoissonSampledDpEvent(0.1, dp_event.GaussianDpEvent(0)),
+      dp_event.PoissonSampledDpEvent(0.1, dp_event.LaplaceDpEvent(0)))
+  def test_additive_noise_mechanisms_with_zero_noise_multiplier(self, event):
+    accountant = pld_privacy_accountant.PLDAccountant()
+    accountant.compose(event)
+    self.assertEqual(accountant.get_delta(1.0), 1)
+    self.assertEqual(accountant.get_epsilon(0.01), math.inf)
+
+  @parameterized.parameters(
+      dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(1)),
+      dp_event.PoissonSampledDpEvent(0, dp_event.LaplaceDpEvent(1)),
+      dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(0)),
+      dp_event.PoissonSampledDpEvent(0, dp_event.LaplaceDpEvent(0)))
+  def test_poisson_subsampling_with_zero_probability(self, event):
+    accountant = pld_privacy_accountant.PLDAccountant()
+    accountant.compose(event)
+    self.assertEqual(accountant.get_delta(0), 0)
+    self.assertEqual(accountant.get_epsilon(0), 0)
+
   def test_gaussian_basic(self):
     gaussian_event = dp_event.GaussianDpEvent(noise_multiplier=math.sqrt(3))
     accountant = pld_privacy_accountant.PLDAccountant()
diff --git a/python/dp_accounting/rdp/rdp_privacy_accountant.py b/python/dp_accounting/rdp/rdp_privacy_accountant.py
index f1825fb..16335fe 100644
--- a/python/dp_accounting/rdp/rdp_privacy_accountant.py
+++ b/python/dp_accounting/rdp/rdp_privacy_accountant.py
@@ -347,12 +347,12 @@
   """
 
   def compute_one_order(q, alpha):
-    if np.isinf(alpha) or noise_multiplier == 0:
-      return np.inf
-
     if q == 0:
       return 0
 
+    if np.isinf(alpha) or noise_multiplier == 0:
+      return np.inf
+
     if q == 1.:
       return alpha / (2 * noise_multiplier**2)
 
diff --git a/python/dp_accounting/rdp/rdp_privacy_accountant_test.py b/python/dp_accounting/rdp/rdp_privacy_accountant_test.py
index b6291b1..1e60ea8 100644
--- a/python/dp_accounting/rdp/rdp_privacy_accountant_test.py
+++ b/python/dp_accounting/rdp/rdp_privacy_accountant_test.py
@@ -171,10 +171,12 @@
     self.assertAlmostEqual(rdp_with_heterogeneous_compose,
                            base_rdp + base_rdp_2)
 
-  def test_zero_poisson_sample(self):
+  @parameterized.parameters(
+      dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(1.0)),
+      dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(0.0)))
+  def test_zero_poisson_sample(self, event):
     accountant = rdp_privacy_accountant.RdpAccountant([3.14159])
-    accountant.compose(
-        dp_event.PoissonSampledDpEvent(0, dp_event.GaussianDpEvent(1.0)))
+    accountant.compose(event)
     self.assertEqual(accountant.get_epsilon(1e-10), 0)
     self.assertEqual(accountant.get_delta(1e-10), 0)